Cursor Rules for Clean Architecture: Boundaries, Modules, and Checks
Adaptable Cursor rules for clean architecture projects: dependency direction, module responsibilities, interface rules, and architecture checks, with fenbs preserving the reasons and exceptions behind them.
Updated 4 min read
Cursor rules for clean architecture prevent a coding assistant from crossing the dependency boundaries your team has drawn. Without them, an assistant writes working code that imports a database adapter in the domain layer, calls a framework function from a use case, or adds an exception to a module rule without knowing an exception already exists. The boundary violations compile and pass tests but accumulate into the architecture problem the rules were meant to prevent. fenbs preserves the decisions behind those rules, including the exceptions that were deliberately allowed, so the next assistant reads both the rule and its context before proposing a change.
The rules in this guide are written to be adaptable. Clean architecture projects differ in naming, layer structure, and technology. Adjust the layer names and import paths to match your project before using these templates.
What clean architecture rules need to say
The core of clean architecture is the dependency rule: inner layers know nothing about outer ones. An entity does not import a repository implementation. A use case does not know which HTTP framework delivers its input. Rules that enforce this need to be specific about which directories represent which layers and which imports are forbidden.
- Dependency direction: which layer may import from which; forbidden import paths stated explicitly.
- Module responsibilities: what each module is responsible for and what it is not allowed to do.
- Interface placement: where interfaces and ports are defined, and where implementations live.
- Exception policy: how to record a deliberate boundary exception, who approved it, and why.
- Architecture check: whether the project uses a tool to verify boundaries automatically.
Dependency direction rules
The most useful dependency rule is one that names actual import paths, not layer concepts. "The domain layer must not import from the infrastructure layer" is correct but hard for an assistant to apply if it does not know which files are domain and which are infrastructure. Name the directories.
# Dependency direction rules ## Layer directories - domain/ Core business entities and domain services. No framework or library imports. - application/ Use cases and application services. May import from domain/ only. - infrastructure/ Adapters, repositories, external services. May import from domain/ and application/. - presentation/ Controllers, routes, UI. May import from application/ and infrastructure/. ## Forbidden imports - domain/ must never import from application/, infrastructure/, or presentation/. - application/ must never import from infrastructure/ or presentation/. - Do not import a repository implementation class in a use case; use the repository interface. ## Cross-layer communication Use interfaces (ports) defined in application/ or domain/. Implementations in infrastructure/ implement those interfaces. Dependency injection wires the implementation to the interface at startup. ## Checking This project uses dependency-cruiser. Run `npx depcruise src` before a PR. If the cruiser reports a violation, fix the import or record an approved exception in .dependency-cruiser.cjs.
Module responsibility rules
Even within a layer, modules can drift. A repository that formats output for a view, a use case that validates raw HTTP input, or a domain service that logs to a framework logger all cross an invisible line. Rules that describe what each module is responsible for and what it is explicitly not responsible for give an assistant a clear decision boundary.
# Module responsibilities ## Domain entities Responsible for: encapsulating business rules, validation of core invariants, identity. Not responsible for: persistence, serialization to API formats, logging. Example: an Order entity validates that line items are not empty. It does not write to the database. ## Use cases Responsible for: orchestrating domain objects to complete one business operation, calling repository interfaces, publishing domain events. Not responsible for: HTTP request parsing, response formatting, authentication. Example: a PlaceOrderUseCase validates the order, calls the inventory repository, and publishes an OrderPlaced event. It does not parse a request body. ## Repositories Responsible for: translating between domain objects and persistence storage. Not responsible for: business validation, authorization, or calling other use cases. A repository method name reflects a domain operation (findByCustomerId), not a SQL operation (selectWhere).
Interface and abstraction rules
Interfaces defined in the right layer are what make the architecture testable. An application layer interface for a payment gateway means a use case test can use a fake gateway without standing up the real service. Rules that state where interfaces live and what they should look like make testing a natural consequence of the architecture, not an afterthought.
# Interfaces and ports ## Interface placement Define interfaces (ports) in the same layer as their consumers: - Repository interfaces in application/ (used by use cases). - Event publisher interfaces in domain/ (used by domain services). - External service interfaces in application/ (email, payment, storage). ## Interface naming Suffix interfaces with Port or Repository as appropriate: CustomerRepository, EmailPort. Name methods from the consumer's perspective: send(message), not callSendgridAPI(message). ## Implementation placement Implementations (adapters) live in infrastructure/. Name them with the technology: PostgresCustomerRepository, SendgridEmailAdapter. ## Testing Provide a fake or in-memory implementation for every interface used by use cases. Place fakes in tests/fakes/. Use these in unit tests; use the real implementation in integration tests.
Keeping rules in sync with tooling
A Cursor rule that describes dependency direction is most effective when it is backed by a static analysis tool that enforces the same rule automatically. Tools like dependency-cruiser for Node.js, ArchUnit for Java, or import-linter for Python can check actual imports against a defined policy and fail a build when a rule is violated. The Cursor rule tells the assistant what to write; the tool catches what slipped through. Mention the tool in the rule itself: "Before completing any architecture change, run the dependency checker and fix all violations." This makes the check part of the workflow rather than a separate review step.
Recording exceptions to architecture rules
Every long-running project has deliberate boundary exceptions: a performance optimization that required a direct database call in a use case, a third-party library that does not fit neatly into a layer. The problem is not the exception but the undocumented exception. An assistant reading the rules without knowing about the exception will either flag it as wrong or silently copy the pattern. Record exceptions where they can be found.
In the rule file, add an exception section with the violation, the reason, and a reference to the decision. In fenbs, record it as a Decision with the person who approved it. The rule file and fenbs together give any assistant the current state: the boundary, and the approved deviations from it.
Keep the reasons behind your architecture boundaries in fenbs
fenbs records Decisions and rules with the person who made them and why. When the team decides that a use case may call a caching layer directly for performance reasons, record it as a Decision in fenbs with the reason and any conditions. The Cursor rule file says what the boundary is; fenbs explains why it is there and what exceptions are approved. The next session sees both.
Create a free accountFree for one person · No credit card · Works with Claude, ChatGPT, Cursor and more
How to do this with fenbs
- Create a free account and add a project for the codebase.
- Record the core architecture decisions in fenbs: which directories represent which layers, how interfaces are named, and which exceptions are deliberately approved. Each decision should name who agreed to it and why.
- Add the Cursor rule files to
.cursor/rules/. Start with the dependency direction rules, since those are the most frequently violated. Add the module responsibility rules next. - Connect Cursor to fenbs over MCP from Integrations. Ask it to read the project context before proposing a change that touches an interface or a layer boundary.
- When a new exception to the architecture is agreed on, record it in fenbs and update the rule file. The two should always match.