Clean Architecture in TypeScript holds only when a failing check blocks a violating import. Folder names, diagrams and code review memory do not do that. The working recipe has three parts. Write the allowed dependency directions as a small matrix. Encode that matrix in a rule engine (Nx module boundaries, dependency-cruiser, or both with TypeScript project references). Run the engine as a required CI check. This guide covers each step, the tool trade-offs, and the bypass paths that quietly defeat a green build.
Step 1: Write the dependency rule down before choosing a tool
Name the layers using the smallest vocabulary that fits your system, then list which layer may import which. A common starting matrix:
As an Amazon Associate I earn from qualifying purchases.
| Source layer | May depend on |
|---|---|
| Domain | Domain only |
| Application (use cases) | Application, domain |
| Adapters (HTTP, persistence, messaging) | Adapters, application, domain |
| Composition root | Anything |
This is an example, not a universal schema. Decide explicitly how tests, generated code, shared utilities and package manifests are treated. Each usually needs its own row.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Two design points make the matrix workable. First, an interface that a use case needs (a repository, a clock, a mailer) belongs to the policy layer that needs it. The outer adapter implements it. Second, wiring implementations to interfaces happens at the outermost edge, the composition root. The test of success: swapping a database or web framework should never force the domain to import something new.
#1 Best Overall
Step 2: Pick the enforcement that matches your repository
| Approach | Best fit | What it does | Limits |
|---|---|---|---|
Nx @nx/enforce-module-boundaries ESLint rule |
Nx repositories split into tagged projects | Checks TypeScript/JavaScript imports and package dependencies during lint; depConstraints say which tags may depend on which; external packages can be allowed or banned. |
Import and package oriented, for JS/TS projects. Nx documents its Oxlint integration as experimental. |
Nx Conformance enforce-project-boundaries |
Nx workspaces needing graph checks beyond the lint rule or across languages | Checks dependencies in the Nx graph using the same tag-constraint model. | Requires Nx Enterprise. |
| dependency-cruiser | Repos without Nx, or needing file/path-level custom rules | Rules can forbid, allow or require dependencies; an error severity makes the command exit non-zero. |
You write the rules and must confirm its resolution matches your build. |
| TypeScript project references | Splitting build projects | Structures programs into smaller projects; tsc --build builds referenced projects in dependency order. |
Not a complete architecture linter. |
These are complementary, not interchangeable. Sources: Nx boundary overview, dependency-cruiser rules reference, TypeScript Project References.
Option A: Nx tag constraints
Tag each project with its layer, for example layer:domain, layer:application, layer:adapter and layer:composition. Then configure @nx/enforce-module-boundaries in ESLint so each source tag lists its allowed target tags (for instance, layer:domain may depend only on layer:domain). Exact configuration syntax depends on your Nx version, so copy from the current rule options rather than from old blog posts.
Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
Ban frameworks from core projects
Layer-to-layer edges are only half the story. Domain code can also import an ORM or web framework directly from npm. Nx supports allowing or banning external packages per tag, and its guide uses keeping domain logic free of infrastructure as the example: see external import constraints. Use this for domain and application projects.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Watch for defeating wildcards
A catch-all entry or a permissive allow pattern can make the whole rule meaningless. Review the config for wildcards. Nx also advises keeping the number of project types small and their meanings clear (Project Dependency Rules).
Option B: dependency-cruiser outside Nx
dependency-cruiser reads your sources, builds the dependency graph and evaluates forbidden, allowed and required rules. Rules with severity: "error" make the CLI exit non-zero, which is what lets CI fail. A forbidden rule for the domain looks like this in concept: name it domain-not-outward, set from.path to your domain folder, and set to.path to the adapters, infrastructure or framework folders and packages. Check the rules reference for the exact field names in your version.
Before trusting it, confirm the configuration is TypeScript-aware for your repository: path aliases resolve, type-only imports are treated the way you intend, and dynamic imports show up.
Where TypeScript project references fit
Project references let you split a codebase into smaller TypeScript projects and express logical groupings. tsc --build finds and builds referenced projects in dependency order, while plain tsc -p does not build dependencies for you. They also bring declaration output and editor and clone workflow considerations. They are a useful structural backbone, but treat them as build organization, not as the policy engine. Import rules such as “application must not import adapter code” still deserve a dedicated checker. Also remember that types constrain assignability, not the intended source dependency graph.
Free tools Windows power users keep installed
One-click scans. No signup required.
Roll it out without breaking the team
- Draw the current dependency graph and label each module with a layer.
- Write the allowed-edge matrix from Step 1 and pick the tool.
- Run the check locally and in CI in a reporting mode first, if the tool allows it.
- Classify existing violations: fix them, or record narrow temporary exceptions.
- Switch the rule to error severity and make the CI job required, so merges cannot bypass it. For Nx, that job is your lint target.
- Delete migration exceptions as the work completes. Keep any permanent exception in config with a comment giving its owner and reason or expiry.
Probe the bypass paths
A green check proves compliance only with the rules you configured. No tool should be assumed to see every edge, so for each important rule commit a small intentional violation in a scratch branch and confirm the build goes red. Cover these cases:
Quick Recap
Best Value
- Deep relative imports that go around a project’s public entry point.
- Path aliases that the checker may not resolve.
- Package exports and externally published packages.
- Re-exports and barrels that hide the true origin of a dependency.
- Type-only imports, which are erased at runtime but still couple source.
- Dynamic imports and runtime loading.
- Tests and generated code, which often receive blanket exemptions.
Common mistakes
- Relying on folder names or diagrams with no failing check.
- One broad
sharedtag that lets business policy reach infrastructure through a “neutral” utility package. Split shared code by layer. - Checking local project edges but forgetting external package imports.
- Treating a project reference as an import boundary rule.
- Leaving temporary suppressions in place indefinitely.
- Assuming Nx Conformance is available to every Nx user (it requires Nx Enterprise), or calling the Oxlint integration stable (Nx labels it experimental).
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




