Comprehensive rules for implementing Domain-Driven Design with Clean Architecture and CQRS in .NET 8.
You've seen the codebase disasters. Massive controllers stuffed with business logic. Data models masquerading as domain objects. Transaction scripts everywhere. Your backend has become a maintenance nightmare where every change ripples through dozens of unrelated files.
These Cursor Rules implement battle-tested Domain-Driven Design patterns that Fortune 500 companies use to build maintainable, scalable .NET backends. No theoretical fluff—just proven architectural patterns that solve real enterprise development problems.
Your current challenges aren't unique:
Domain Logic Scattered Everywhere: Business rules live in controllers, services, repositories, and view models. Finding where pricing calculations happen requires a treasure hunt through your codebase.
Tight Coupling Nightmares: Changing your data model breaks your API contracts. Adding a new business rule requires touching 12 different classes across 4 different layers.
Testing Complexity: Unit testing requires spinning up databases, mocking HTTP clients, and fighting with dependency injection containers just to test a simple business calculation.
Scaling Bottlenecks: Your monolithic application can't scale reads independently from writes. Every new feature slows down the entire system.
These rules establish a clean architectural foundation that separates concerns properly:
// Before: Anemic model with scattered business logic
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public string Status { get; set; }
public List<OrderItem> Items { get; set; }
}
// After: Rich domain model with encapsulated behavior
public class Order : AggregateRoot
{
private readonly List<OrderItem> _items = new();
public OrderId Id { get; private set; }
public Money Total { get; private set; }
public OrderStatus Status { get; private set; }
public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly();
public void AddItem(Product product, Quantity quantity)
{
if (Status != OrderStatus.Draft)
throw new OrderAlreadySubmittedException();
var item = new OrderItem(product, quantity);
_items.Add(item);
RecalculateTotal();
RaiseDomainEvent(new ItemAddedToOrder(Id, item.ProductId, quantity));
}
}
The rules enforce Clean Architecture with proper dependency direction, CQRS for scalability, and domain events for loose coupling between bounded contexts.
Eliminate Context Switching: Domain logic lives exactly where you expect it. Need to modify order validation? It's in the Order aggregate. Need to add a new business rule? One place, one change.
Faster Feature Development: New features typically require changes in just one bounded context. Add a new shipping method without touching payment or inventory code.
Simplified Testing: Test pure domain logic without databases or external dependencies:
[Fact]
public void Should_Calculate_Shipping_Cost_Based_On_Weight()
{
// Given
var order = OrderBuilder.New()
.WithItem(Product.Laptop, Quantity.Of(2))
.Build();
// When
var shippingCost = order.CalculateShipping(ShippingZone.International);
// Then
shippingCost.Should().Be(Money.Dollars(45.50m));
}
Performance at Scale: CQRS separation means you can horizontally scale your read models while keeping write operations consistent. Cache read models aggressively without affecting domain integrity.
# Create new solution structure
dotnet new sln -n YourDomain
mkdir src
cd src
# Create Clean Architecture projects
dotnet new classlib -n YourDomain.Domain
dotnet new classlib -n YourDomain.Application
dotnet new classlib -n YourDomain.Infrastructure
dotnet new webapi -n YourDomain.Api
Copy the DDD Clean Architecture rules to your .cursorrules file in your solution root. The rules will automatically guide code generation following Clean Architecture patterns.
src/
├─ OrderManagement.Domain/
│ ├─ Aggregates/
│ ├─ ValueObjects/
│ ├─ DomainEvents/
│ └─ Exceptions/
├─ OrderManagement.Application/
│ ├─ Commands/
│ ├─ Queries/
│ └─ Handlers/
├─ OrderManagement.Infrastructure/
│ ├─ Persistence/
│ ├─ Messaging/
│ └─ ExternalServices/
└─ OrderManagement.Api/
├─ Controllers/
└─ Middleware/
Ask Cursor to generate your first aggregate root. The rules ensure proper encapsulation, invariant enforcement, and domain event publication.
Implement command and query handlers using MediatR. The rules automatically separate read and write concerns with proper validation pipelines.
Development Speed: Teams report 40-60% faster feature development once the initial architecture is established. New features typically touch fewer files and require less coordination between developers.
Code Quality: Domain logic is isolated, testable, and follows business terminology. Code reviews focus on business rules rather than architectural concerns.
System Reliability: Bounded contexts prevent cascading failures. Event-driven architecture provides natural retry mechanisms and fault tolerance.
Team Collaboration: Ubiquitous language bridges the gap between business stakeholders and developers. Domain models become living documentation.
Technical Debt Reduction: Clean Architecture prevents the accumulation of technical debt. Changes are localized to appropriate layers and contexts.
Scalability Gains: CQRS enables independent scaling of read and write operations. Most applications see 3-5x improvement in read performance with proper read model optimization.
The rules transform chaotic enterprise backends into maintainable, scalable systems that directly reflect business capabilities. Your team will spend more time solving business problems and less time fighting architectural complexity.
Stop treating your domain model as a data container. Start building software that business stakeholders can actually understand and maintain.
You are an expert in Domain-Driven Design (DDD), .NET 8, C#, Clean Architecture, CQRS, Event Sourcing, Kubernetes, and modern DevOps tooling.
Key Principles
- Model the core domain first; everything else supports it.
- Every boundary (API, message, DB) is driven by the domain model, never the other way around.
- Respect Bounded Contexts; do not share domain types across context boundaries.
- Ubiquitous Language is mandatory: class, method, and file names mirror business terminology.
- Prefer immutability and pure functions in domain code; state changes occur only through Aggregate roots.
- Favor composition over inheritance; avoid anemic domain models.
- Keep infrastructure replaceable via dependency inversion.
C#
- Target .NET 8, LangVersion = preview when useful; enable nullable reference types.
- Folder scheme per Clean Architecture:
src/
├─ <ContextName>.Domain
├─ <ContextName>.Application
├─ <ContextName>.Infrastructure
└─ <ContextName>.Api
- Namespaces align with folders and end in the layer name (e.g., Shipping.Domain). No "Utils" namespaces.
- Use record structs for Value Objects; mark as readonly and implement IComparable where ordering matters.
```
public readonly record struct Money(decimal Amount, string Currency)
{
public static Money operator +(Money a, Money b)
{
if (a.Currency != b.Currency) throw new CurrencyMismatchException();
return new Money(a.Amount + b.Amount, a.Currency);
}
}
```
- Aggregate Roots expose behavior, not data. All collections are private and modified via methods.
- Commands/Queries are simple POCOs in the Application layer; handlers live in the same folder.
- Prefer MediatR’s pipeline for cross-cutting concerns (validation, logging, retries).
- Use asynchronous streams (IAsyncEnumerable<T>) for long running reads.
Error Handling and Validation
- Validate invariants inside Aggregates; throw only domain-specific exceptions (derive from DomainException).
- Map DomainException → ProblemDetails in an API middleware.
- Always publish a DomainEvent after a successful Aggregate state change; keep events immutable.
- Fail fast: guard clauses at the top of methods; main logic at the end.
- Use Result<T> (OneOf / FluentResults) in Application layer when business flow can recover from an error.
Framework-Specific Rules (.NET 8 + Clean Architecture)
- Dependency Direction: Domain → Application → Infrastructure → API. No reverse references.
- CQRS implementation:
• Command → CommandHandler → Aggregate → DomainEvents.
• Query → QueryHandler → read model (EF Core or Dapper).
- Use EF Core only in Infrastructure. Repositories return Aggregates, not ORM entities.
- Event Sourcing (optional heavy domain):
• Store raw events in EventStoreDB.
• Rehydrate Aggregates via event streams; never load snapshots directly inside business logic.
• Keep projections/read-models in separate database optimized for reads.
- Messaging: publish DomainEvents via MassTransit → RabbitMQ/Kafka. Consumers live in Application layer of another Bounded Context.
- Version your messages; never break contracts.
Testing
- Unit: Test Aggregates with Given/When/Then style (xUnit).
- Integration: Use TestContainers + Docker to spin up EventStoreDB, SQL, or RabbitMQ per test run.
- Contract: CDC with Pact to enforce message schemas between Bounded Contexts.
- Use builder pattern for complex Aggregate setup; avoid test fixtures that share state.
Performance
- Apply CQRS: scale read side (horizontal replicas) separately from write side.
- Employ caching at read model layer (Redis) but never inside domain code.
- Use async EF Core queries with cancellation tokens; always set NoTracking for read models.
- Profile hot paths with dotnet-trace; never guess.
Security
- Enforce authentication/authorization in API layer only; domain code is security-agnostic.
- Use Minimal APIs + policies for endpoint ACLs; include correlation IDs in logs.
- Encrypt sensitive events at rest; version key rotations.
Documentation
- Maintain a living Context Map in Miro/Lucidchart committed under /docs.
- Each Aggregate root has a README explaining invariants and public behavior.
Common Pitfalls
- Leaking infrastructure concerns (e.g., DbContext) into Domain/Application layers.
- Transaction scripts masquerading as Aggregates.
- Over-normalizing Value Objects—only model what matters.
- Synchronous REST calls across Bounded Contexts; prefer asynchronous messaging.