.NET Architecture
Preserve useful dependency boundaries while minimizing ceremony. Treat architecture as a set of
testable ownership and dependency decisions, not a required folder diagram or package list.
Establish The Existing Contract
- Read repository instructions, architecture decisions, solution/project files, shared MSBuild
configuration, composition roots, representative features, and tests.
- Map project references and important package dependencies. Identify which code owns business
policy, application orchestration, delivery contracts, persistence, and external integrations.
- Trace one comparable feature end to end before proposing a new pattern.
- State whether the task preserves the existing architecture, repairs a violation, or deliberately
changes the model. Do not smuggle an architectural migration into an ordinary feature.
For an architecture assessment or redesign, read
references/architecture-selection.md. For an implementation
or review that crosses boundaries, read references/feature-change-map.md.
For dependency and test enforcement, read
references/architecture-enforcement.md.
Choose The Smallest Honest Boundary Model
- Keep a single project with feature folders when compile-time separation would cost more than it
protects. Use projects or modules when independent compilation, dependency enforcement, ownership,
replacement, or deployment value justifies them.
- Preserve inward dependency direction: business policy should not require delivery frameworks,
persistence, cloud SDKs, or external service implementations.
- Let the outer composition root reference concrete adapters in order to wire them. Do not use DI
resolution as an inward dependency escape hatch.
- Define ports from the needs of the owning policy and in domain/application language. Avoid leaking
DbContext, IQueryable, SDK results, or transport DTOs inward.
- Keep vertical slices cohesive. A feature need not touch every layer, and a shared abstraction needs
demonstrated common semantics rather than similar spelling.
Implement Behavior Through The Boundaries
- Put state invariants and transitions in the domain model or an explicitly named policy.
- Keep application handlers focused on orchestration, authorization that depends on current state,
transaction boundaries, and result semantics.
- Keep delivery adapters focused on transport validation/mapping and infrastructure adapters focused
on external translation and resource ownership.
- Use repositories for aggregate persistence where they add a meaningful boundary. Queries may use
direct, bounded projections or read models.
- Treat domain events as facts inside a boundary. If work crosses transactional resources, define
durable publication, idempotency, retry, and duplicate behavior; in-process dispatch is not an
atomic integration guarantee.
- Preserve cancellation, error categories, compatibility, and observability as data crosses ports.
Do not introduce a mediator, CQRS framework, repository abstraction, specification library, result
wrapper, endpoint framework, mapping layer, or value-object generator solely to make the repository
look architectural. Reuse established choices or justify a new dependency from a concrete need.
Verify The Result
- Unit-test domain invariants and application policy independently of hosting and external I/O.
- Contract-test ports and production adapters where boundary semantics matter.
- Integration-test persistence, migrations, transactions, queues, and external clients realistically.
- Functional-test the composition root and delivery behavior for the changed path.
- Run architecture checks for allowed project references, forbidden dependencies, and cycles.
- Run the repository's normal format, build, analyzer, and test gates.
Report the resulting boundary map, decisions made, files changed, exact verification commands and
results, and any boundary that remains unverified.
Attribution
Substantially adapted from architectural patterns and cautions reviewed in
ardalis/CleanArchitecture, commit
fbdc0951879f5e8dca1bebc273d4b28cb2934469, MIT license. This skill generalizes the upstream full
and minimal templates, removes framework/package mandates, and adds explicit transaction,
architecture-enforcement, test-isolation, and evolutionary-design guidance.
1---2name: dotnet-architecture3description: Assess, design, implement, or review architectural boundaries in a .NET application using Clean Architecture, ports and adapters, onion architecture, modular monolith, or vertical slices. Use when a task changes project dependencies, domain/application/infrastructure ownership, use-case flow, repositories or query models, domain events, composition roots, or architecture tests. Do not use for a local C# edit whose boundary ownership is already clear.4---56# .NET Architecture78Preserve useful dependency boundaries while minimizing ceremony. Treat architecture as a set of9testable ownership and dependency decisions, not a required folder diagram or package list.1011## Establish The Existing Contract12131. Read repository instructions, architecture decisions, solution/project files, shared MSBuild14 configuration, composition roots, representative features, and tests.152. Map project references and important package dependencies. Identify which code owns business16 policy, application orchestration, delivery contracts, persistence, and external integrations.173. Trace one comparable feature end to end before proposing a new pattern.184. State whether the task preserves the existing architecture, repairs a violation, or deliberately19 changes the model. Do not smuggle an architectural migration into an ordinary feature.2021For an architecture assessment or redesign, read22[references/architecture-selection.md](references/architecture-selection.md). For an implementation23or review that crosses boundaries, read [references/feature-change-map.md](references/feature-change-map.md).24For dependency and test enforcement, read25[references/architecture-enforcement.md](references/architecture-enforcement.md).2627## Choose The Smallest Honest Boundary Model2829- Keep a single project with feature folders when compile-time separation would cost more than it30 protects. Use projects or modules when independent compilation, dependency enforcement, ownership,31 replacement, or deployment value justifies them.32- Preserve inward dependency direction: business policy should not require delivery frameworks,33 persistence, cloud SDKs, or external service implementations.34- Let the outer composition root reference concrete adapters in order to wire them. Do not use DI35 resolution as an inward dependency escape hatch.36- Define ports from the needs of the owning policy and in domain/application language. Avoid leaking37 `DbContext`, `IQueryable`, SDK results, or transport DTOs inward.38- Keep vertical slices cohesive. A feature need not touch every layer, and a shared abstraction needs39 demonstrated common semantics rather than similar spelling.4041## Implement Behavior Through The Boundaries4243- Put state invariants and transitions in the domain model or an explicitly named policy.44- Keep application handlers focused on orchestration, authorization that depends on current state,45 transaction boundaries, and result semantics.46- Keep delivery adapters focused on transport validation/mapping and infrastructure adapters focused47 on external translation and resource ownership.48- Use repositories for aggregate persistence where they add a meaningful boundary. Queries may use49 direct, bounded projections or read models.50- Treat domain events as facts inside a boundary. If work crosses transactional resources, define51 durable publication, idempotency, retry, and duplicate behavior; in-process dispatch is not an52 atomic integration guarantee.53- Preserve cancellation, error categories, compatibility, and observability as data crosses ports.5455Do not introduce a mediator, CQRS framework, repository abstraction, specification library, result56wrapper, endpoint framework, mapping layer, or value-object generator solely to make the repository57look architectural. Reuse established choices or justify a new dependency from a concrete need.5859## Verify The Result60611. Unit-test domain invariants and application policy independently of hosting and external I/O.622. Contract-test ports and production adapters where boundary semantics matter.633. Integration-test persistence, migrations, transactions, queues, and external clients realistically.644. Functional-test the composition root and delivery behavior for the changed path.655. Run architecture checks for allowed project references, forbidden dependencies, and cycles.666. Run the repository's normal format, build, analyzer, and test gates.6768Report the resulting boundary map, decisions made, files changed, exact verification commands and69results, and any boundary that remains unverified.7071## Attribution7273Substantially adapted from architectural patterns and cautions reviewed in74[`ardalis/CleanArchitecture`](https://github.com/ardalis/CleanArchitecture), commit75`fbdc0951879f5e8dca1bebc273d4b28cb2934469`, MIT license. This skill generalizes the upstream full76and minimal templates, removes framework/package mandates, and adds explicit transaction,77architecture-enforcement, test-isolation, and evolutionary-design guidance.