Domain Layer Architecture
The Ordering domain implements Domain-Driven Design tactical patterns with a focus on:- Rich Domain Models: Business logic encapsulated in entities
- Aggregate Boundaries: Order as the consistency boundary
- Value Objects: Immutable primitives with validation
- Domain Events: Decoupled domain logic
- Invariant Protection: Guard clauses and private setters
Base Abstractions
Entity Base Class
All entities inherit fromEntity<T> with strongly-typed identifiers and audit fields:
Aggregate Base Class
Aggregates manage domain events and define transactional boundaries:- Collects domain events during aggregate operations
- Events are dispatched by
DispatchDomainEventsInterceptorduring SaveChanges - Ensures events are only published when changes are persisted
- Maintains transactional consistency
Aggregates
Order Aggregate Root
TheOrder is the main aggregate root, defining the consistency boundary for order operations:
- Factory Method:
Create()ensures valid initial state - Encapsulation: Private setters prevent invalid state changes
- Invariant Protection: Guard clauses in
Add()method - Domain Events: Raises
OrderCreatedEventandOrderUpdatedEvent - Calculated Properties:
TotalPricecomputed from order items - Collection Management: OrderItems exposed as read-only, modified through methods
OrderItem Entity
Order line items are entities within the Order aggregate:- Internal Constructor: Can only be created by the Order aggregate
- Immutable: No update methods; modifications require remove and re-add
- Strongly-Typed IDs: Uses value objects for type safety
Customer Entity
Customer is a separate aggregate root:Product Entity
Product is a separate aggregate root for catalog information:Value Objects
Value objects are immutable and defined by their properties, not identity.Strongly-Typed Identifiers
All entity IDs are value objects preventing primitive obsession:- Type Safety: Cannot accidentally pass wrong ID type
- Validation: Ensures IDs are never empty
- Domain Semantics: Clear intent in method signatures
- Refactoring: Easy to find all usages of specific ID types
OrderName Value Object
Wraps the order name with validation:Address Value Object
Immutable address with comprehensive validation:- Record Type: Structural equality by default
- Immutability: All properties are init-only
- Factory Method:
Of()ensures valid construction - Guard Clauses: Validates required fields
Payment Value Object
Secure payment information with validation:Enumerations
OrderStatus
Defines the lifecycle states of an order:Domain Events
Domain events represent significant occurrences within the domain.OrderCreatedEvent
Raised when a new order is created:OrderUpdatedEvent
Raised when an order is updated:Domain Exceptions
Domain Event Dispatch
Domain events are automatically dispatched during database SaveChanges through theDispatchDomainEventsInterceptor:
- Aggregate raises domain event during operation
- Event is stored in aggregate’s event collection
- When
SaveChangesis called, interceptor extracts all events - Events are cleared from aggregates
- Events are published via MediatR
- Domain event handlers execute
- Database transaction commits
Design Principles Applied
Encapsulation
- Private setters prevent unauthorized state changes
- Factory methods control object creation
- Collection properties exposed as read-only
Invariant Protection
- Guard clauses in factory methods and operations
- Value object validation in
Of()methods - Business rules enforced in aggregate methods
Ubiquitous Language
- Domain terms reflected in code (Order, OrderItem, Customer)
- Value objects capture domain concepts (OrderName, Address, Payment)
- Methods use domain language (Add, Remove, Update, not generic CRUD)
Aggregate Boundaries
- Order is the consistency boundary
- OrderItems can only be modified through Order
- Customer and Product are separate aggregates
- No cross-aggregate references except by ID
Next Steps
- Application Layer - See how domain models are used in CQRS commands and queries
- API Reference - Explore the REST endpoints that interact with the domain
