clean-architecture-layers
Enforce dependency direction in clean architecture — inner layers must not reference outer layers.
Rule details
Section titled “Rule details”Clean architecture mandates that dependencies point inward: API → Application → Domain. The Domain layer must have zero external dependencies, the Application layer must not reference Infrastructure directly, and no lower layer should reference the API project. This rule checks using directives (C#) or import statements to enforce these boundaries.
The same pattern applies to any layered architecture in any language — adjust the import patterns and layer paths accordingly.
Examples of incorrect code
Section titled “Examples of incorrect code”using Microsoft.EntityFrameworkCore; // ✗ Domain → Infrastructureusing StavangerChallenge.Infrastructure; // ✗ Domain → Infrastructureusing StavangerChallenge.Infrastructure; // ✗ Application → Infrastructureusing StavangerChallenge.API; // ✗ Application → APIExamples of correct code
Section titled “Examples of correct code”namespace StavangerChallenge.Domain.Entities;// No external dependencies — pure domain logicusing StavangerChallenge.Domain.Entities; // ✓ Application → Domain// Uses IRepository<T> interface, not Infrastructure directlyRule implementation
Section titled “Rule implementation”/// <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 projects, adapt the patterns:
// 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\//When to use it
Section titled “When to use it”When your project follows clean architecture, hexagonal architecture, or any layered architecture where dependency direction must be enforced.
When not to use it
Section titled “When not to use it”When your project does not use a layered architecture, or when layers are not organized into distinct directories.