Skip to content

Commit

Permalink
add arch-unit-ts
Browse files Browse the repository at this point in the history
  • Loading branch information
Johannbr committed Nov 14, 2024
1 parent 280b2e6 commit 9d1f935
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 544 deletions.
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,5 @@
"jestCommandLine": "source ~/.zshrc && nvm use && npm run test --",
"runMode": "watch"
}
],
"typescript.tsdk": "node_modules/typescript/lib"
]
}
4 changes: 4 additions & 0 deletions apiv2/arch-unit-ts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"showImportsWarning": false,
"failOnEmptyShould": false
}
2 changes: 1 addition & 1 deletion apiv2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"arch-unit-ts": "^1.0.8",
"eslint": "^8.42.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
Expand All @@ -69,7 +70,6 @@
"ts-jest": "^29.2.5",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsarch": "^5.4.0",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.6.3"
},
Expand Down
1 change: 0 additions & 1 deletion apiv2/src/admin/infra/iam/auth/ReferentAuth.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { AuthProvider } from "./Auth.provider";
import * as bcrypt from "bcrypt";
import { ReferentMapper } from "../repository/mongo/Referent.mapper";
import { TechnicalException, TechnicalExceptionType } from "@shared/infra/TechnicalException";
import { error } from "console";

@Injectable()
export class ReferentAuthFacade implements ReferentAuthGateway {
Expand Down
9 changes: 3 additions & 6 deletions apiv2/src/mainJob.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import "./instrument"; // first

import { Logger } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { NotificationJobModule } from "./notification/NotificationJob.module";
import { EmailConsumer } from "./notification/infra/email/Email.consumer";
import { Logger } from "@nestjs/common";

async function bootstrap() {
const app = await NestFactory.createApplicationContext(NotificationJobModule);
const notificationConsumer = app.get(EmailConsumer);
const logger = new Logger("bootstrap mainJob");
Logger.log(`Job from class ${notificationConsumer.constructor?.name} started`, "bootstrap mainJob");
await NestFactory.createApplicationContext(NotificationJobModule);
Logger.log(`Job started`, "bootstrap mainJob");
// TODO: handle logs error here ?
// process.on("uncaughtException", (error) => {
// // Use same format as : AllExceptionsFilter.processLog
Expand Down
6 changes: 5 additions & 1 deletion apiv2/src/notification/NotificationJob.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ import { contactFactory, emailFactory } from "./infra/email/brevo/EmailContact.f
],
providers: [Logger, EmailConsumer, ContactConsumer, emailFactory, contactFactory],
})
export class NotificationJobModule {}
export class NotificationJobModule {
constructor(private logger: Logger) {
this.logger.log("NotificationJobModule has started", "NotificationJobModule");
}
}
136 changes: 136 additions & 0 deletions apiv2/test/architecture/Architecture.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { RelativePath } from "arch-unit-ts/dist/arch-unit/core/domain/RelativePath";
import { TypeScriptProject } from "arch-unit-ts/dist/arch-unit/core/domain/TypeScriptProject";
import { Architectures } from "arch-unit-ts/dist/arch-unit/library/Architectures";
import { classes, noClasses } from "arch-unit-ts/dist/main";
import { MatchingPattern } from "./MatchingPattern";
describe("Architecture test", () => {
const srcProject = new TypeScriptProject(RelativePath.of("src"));

describe("Application", () => {
it("Should not depend on infrastructure", () => {
noClasses()
.that()
.resideInAPackage(MatchingPattern.CORE)
.should()
.dependOnClassesThat()
.resideInAnyPackage(MatchingPattern.INFRA)
.because("core should not depend on infrastructure")
.check(srcProject.allClasses());
});
it("Should only have few dependencies", () => {
classes()
.that()
.resideInAPackage(MatchingPattern.CORE)
.should()
.onlyDependOnClassesThat()
.resideInAnyPackage(
MatchingPattern.SNU_LIB,
MatchingPattern.CORE,
MatchingPattern.NESTJS_COMMON,
MatchingPattern.NESTJS_TESTING,
)
.because("Core should not depend on any other dependencies")
.check(srcProject.allClasses());
});

it("Repository should depend on gateway", async () => {
classes()
.that()
.resideInAPackage(MatchingPattern.INFRA)
.and()
.haveSimpleNameEndingWith(MatchingPattern.REPOSITORY_SUFFIX)
.should()
.dependOnClassesThat()
.haveSimpleNameEndingWith(MatchingPattern.GATEWAY_SUFFIX)
.allowEmptyShould(false)
.because("Repository should implement gateway")
.check(srcProject.allClasses());
});

it("Core should not have some classes named", async () => {
noClasses()
.that()
.resideInAPackage(MatchingPattern.CORE)
.should()
.haveSimpleNameEndingWith(MatchingPattern.REPOSITORY_SUFFIX)
.orShould()
.haveSimpleNameEndingWith(MatchingPattern.CONTROLLER_SUFFIX)
.orShould()
.haveSimpleNameEndingWith(MatchingPattern.CONSUMER_SUFFIX)
.orShould()
.haveSimpleNameEndingWith(MatchingPattern.PROVIDER_SUFFIX)
.orShould()
.haveSimpleNameEndingWith(MatchingPattern.FACTORY_SUFFIX)
.orShould()
.haveSimpleNameEndingWith(MatchingPattern.PRODUCER_SUFFIX)
.because("Core should not implement repo, ...")
.check(srcProject.allClasses());
});
});

describe("Admin", () => {
it("Should implement an hexagonal architecture", async () => {
Architectures.layeredArchitecture()
.consideringOnlyDependenciesInAnyPackage(MatchingPattern.ADMIN_CORE, MatchingPattern.ADMIN_INFRA)
.layer("useCase", MatchingPattern.ADMIN_USECASE)
.layer("repository", MatchingPattern.ADMIN_REPOSITORY)
.layer("infra", MatchingPattern.ADMIN_INFRA)
.layer("core", MatchingPattern.ADMIN_CORE)
.whereLayer("useCase")
.mayOnlyBeAccessedByLayers("infra", "useCase")
.whereLayer("repository")
.mayOnlyBeAccessedByLayers("infra")
.whereLayer("infra")
.mayNotBeAccessedByAnyLayer()
.because("Each bounded context should implement an hexagonal architecture")
.check(srcProject.allClasses());
});
it("Should depend on specific dependencies", async () => {
classes()
.that()
.resideInAPackage(MatchingPattern.ADMIN_CORE)
.should()
.onlyDependOnClassesThat()
.resideInAnyPackage(
MatchingPattern.SNU_LIB,
MatchingPattern.NOTIFICATION_CORE,
MatchingPattern.SHARED_CORE,
MatchingPattern.ADMIN_CORE,
MatchingPattern.NESTJS_COMMON,
MatchingPattern.NESTJS_TESTING,
)
.because("Core should not depend on any other dependencies")
.check(srcProject.allClasses());
});
});

describe("Shared", () => {
it("Should depend on specific dependencies", async () => {
classes()
.that()
.resideInAPackage(MatchingPattern.SHARED_CORE)
.should()
.onlyDependOnClassesThat()
.resideInAnyPackage(MatchingPattern.SHARED_CORE, MatchingPattern.NESTJS_COMMON)
.because("Core should not depend on any other dependencies")
.check(srcProject.allClasses());
});
});

describe("BullMQ", () => {
it("Consumer should be in jobModule", async () => {
classes()
.that()
.haveSimpleNameEndingWith(MatchingPattern.CONSUMER_SUFFIX)
.should()
.onlyHaveDependentClassesThat()
.haveSimpleNameEndingWith(MatchingPattern.JOB_MODULE_SUFFIX)
.andShould()
.dependOnClassesThat()
.resideInAPackage(MatchingPattern.NESTJS_BULLMQ)
.allowEmptyShould(false)
.because("Consumer should be in jobModule")
.check(srcProject.allClasses());
});
});
});
27 changes: 27 additions & 0 deletions apiv2/test/architecture/MatchingPattern.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export enum MatchingPattern {
CORE = "..core..",
INFRA = "..infra..",
GATEWAY_SUFFIX = ".gateway.ts",
REPOSITORY_SUFFIX = ".repository.ts",
CONTROLLER_SUFFIX = ".controller.ts",
CONSUMER_SUFFIX = ".consumer.ts",
PROVIDER_SUFFIX = ".provider.ts",
FACTORY_SUFFIX = ".factory.ts",
PRODUCER_SUFFIX = ".producer.ts",
JOB_MODULE_SUFFIX = "Job.module.ts",

ADMIN_CORE = "src.admin.core..",
ADMIN_USECASE = "src.admin..core..useCase..",
ADMIN_INFRA = "src.admin..infra..",
ADMIN_REPOSITORY = "src.admin..infra..repository..",

NOTIFICATION_CORE = "src.notification.core..",

SHARED_CORE = "..shared.core..",

NESTJS_COMMON = "@nestjs.common",
NESTJS_TESTING = "@nestjs.testing",
NESTJS_BULLMQ = "@nestjs.bullmq",

SNU_LIB = "packages",
}
40 changes: 0 additions & 40 deletions apiv2/test/architecture/arch.spec.ts

This file was deleted.

7 changes: 0 additions & 7 deletions apiv2/test/architecture/folder.ts

This file was deleted.

3 changes: 0 additions & 3 deletions apiv2/tsconfig.test.json

This file was deleted.

Loading

0 comments on commit 9d1f935

Please sign in to comment.