Gå til innholdet

clean-architecture-layers

Krev avhengighetsretning i ren arkitektur — indre lag må ikke referere til ytre lag.

Ren arkitektur krever at avhengigheter peker innover: API -> Application -> Domain. Domain-laget må ha null eksterne avhengigheter, Application-laget må ikke referere til Infrastructure direkte, og ingen lavere lag skal referere til API-prosjektet. Denne regelen sjekker using-direktiver (C#) eller import-setninger for å håndheve disse grensene.

Det samme mønsteret gjelder for enhver lagdelt arkitektur i ethvert språk — tilpass importmønstrene og lagstiene deretter.

Domain/Entities/User.cs
using Microsoft.EntityFrameworkCore; // ✗ Domain → Infrastructure
using StavangerChallenge.Infrastructure; // ✗ Domain → Infrastructure
Application/Services/UserService.cs
using StavangerChallenge.Infrastructure; // ✗ Application → Infrastructure
using StavangerChallenge.API; // ✗ Application → API
Domain/Entities/User.cs
namespace StavangerChallenge.Domain.Entities;
// No external dependencies — pure domain logic
Application/Services/UserService.cs
using StavangerChallenge.Domain.Entities; // ✓ Application → Domain
// Uses IRepository<T> interface, not Infrastructure directly
/// <reference path="../rules.d.ts" />
export default {
rules: {
"no-infrastructure-in-domain": {
description:
"Domain layer must not reference Infrastructure or ORM packages",
async check(ctx) {
const domainFiles = await ctx.glob("backend/src/**/Domain/**/*.cs");
for (const file of domainFiles) {
const matches = await ctx.grep(
file,
/using\s+(Microsoft\.EntityFrameworkCore|MyApp\.Infrastructure)/
);
for (const match of matches) {
ctx.report.violation({
message: `Domain must not reference Infrastructure: "${match.content.trim()}"`,
file: match.file,
line: match.line,
fix: "Remove this using directive. Domain entities should have zero external dependencies.",
});
}
}
},
},
"no-infrastructure-in-application": {
description:
"Application layer must not reference Infrastructure directly",
async check(ctx) {
const appFiles = await ctx.glob("backend/src/**/Application/**/*.cs");
for (const file of appFiles) {
const matches = await ctx.grep(file, /using\s+MyApp\.Infrastructure/);
for (const match of matches) {
ctx.report.violation({
message: `Application must not reference Infrastructure: "${match.content.trim()}"`,
file: match.file,
line: match.line,
fix: "Define an interface in Application and implement it in Infrastructure.",
});
}
}
},
},
"no-upward-api-references": {
description: "No layer should reference the API project",
async check(ctx) {
const lowerLayerPatterns = [
"backend/src/**/Domain/**/*.cs",
"backend/src/**/Application/**/*.cs",
"backend/src/**/Infrastructure/**/*.cs",
];
for (const pattern of lowerLayerPatterns) {
const files = await ctx.glob(pattern);
for (const file of files) {
const matches = await ctx.grep(file, /using\s+MyApp\.API/);
for (const match of matches) {
ctx.report.violation({
message: `Lower layers must not reference the API project: "${match.content.trim()}"`,
file: match.file,
line: match.line,
fix: "Dependencies flow inward: API → Application → Domain.",
});
}
}
}
},
},
},
} satisfies RuleSet;

For TypeScript-prosjekter, tilpass mønstrene:

// Check that domain/ does not import from infrastructure/
/import\s+.*from\s+["'].*\/infrastructure\//
// Check that no layer imports from api/
/import\s+.*from\s+["'].*\/api\//

Når prosjektet ditt følger ren arkitektur, heksagonal arkitektur, eller en annen lagdelt arkitektur der avhengighetsretning må håndheves.

Når prosjektet ditt ikke bruker en lagdelt arkitektur, eller når lagene ikke er organisert i separate mapper.