Application Layer Architecture
The Application layer implements CQRS (Command Query Responsibility Segregation) using MediatR, providing:- Commands: State-changing operations (Create, Update, Delete)
- Queries: Read-only operations with optimized projections
- Event Handlers: Domain and integration event processing
- Validation: FluentValidation for input validation
- DTOs: Data transfer objects for API contracts
- Behaviors: Cross-cutting concerns (validation, logging)
Service Registration
- MediatR: Command/query mediator
- ValidationBehavior: Automatic FluentValidation execution
- LoggingBehavior: Request/response logging
- Feature Management: Feature flag support
- Message Broker: MassTransit for event publishing/consumption
Commands
Commands modify system state and follow a consistent pattern.CreateOrderCommand
Creates a new order from customer checkout:- Receives
CreateOrderCommandfrom API - Maps DTO to domain value objects
- Creates Order aggregate using factory method
- Adds OrderItems to aggregate
- Persists to database
- SaveChanges triggers domain event dispatch
- Returns new order ID
UpdateOrderCommand
Updates an existing order:- Retrieves order by ID
- Throws
OrderNotFoundExceptionif not found - Maps DTO to value objects
- Calls
order.Update()method (raisesOrderUpdatedEvent) - Persists changes
- Returns success result
DeleteOrderCommand
Deletes an order:Queries
Queries are read-only operations optimized for data retrieval.GetOrdersQuery
Retrieves orders with pagination:- Pagination support with configurable page size
- Includes related OrderItems
- Sorted by order name
- Maps entities to DTOs
GetOrdersByCustomerQuery
Retrieves all orders for a specific customer:AsNoTracking()for read-only queries- Filtered by customer ID
- Includes order items in single query
GetOrdersByNameQuery
Searches orders by name:- Partial text search using
Contains() - No tracking for performance
- Eager loading of order items
Event Handlers
Domain Event Handlers
Handle domain events raised by aggregates.OrderCreatedEventHandler
Publishes integration event when order is created (see Ordering.Application/Orders/EventHandlers/Domain/OrderCreatedEventHandler.cs:5):- Triggered automatically when order is saved
- Logs domain event
- Checks feature flag
OrderFullfilment - Publishes
OrderDtoas integration event via MassTransit - Other services can consume this event
OrderUpdatedEventHandler
Logs when order is updated (see Ordering.Application/Orders/EventHandlers/Domain/OrderUpdatedEventHandler.cs:2):Integration Event Handlers
Consume events from other services.BasketCheckoutEventHandler
Consumes basket checkout events from Basket service (see Ordering.Application/Orders/EventHandlers/Integration/BasketCheckoutEventHandler.cs:6):- Customer checks out basket in Basket service
- Basket service publishes
BasketCheckoutEventto message broker BasketCheckoutEventHandlerconsumes event- Maps event data to
CreateOrderCommand - Sends command via MediatR
- Order is created through normal command flow
Data Transfer Objects
OrderDto
OrderItemDto
AddressDto
PaymentDto
- DTOs are immutable records
- Used for API contracts and event payloads
- Separated from domain models to prevent leaking domain logic
Database Context Interface
- Application layer depends on abstraction, not EF Core
- Enables unit testing with mock implementations
- Follows Dependency Inversion Principle
Cross-Cutting Concerns
Validation Behavior
Automatically executes FluentValidation validators before command/query execution:- Command/query enters pipeline
- ValidationBehavior executes validators
- Throws ValidationException if validation fails
- Handler executes only if validation passes
Logging Behavior
Logs all command/query requests and responses:CQRS Benefits
Separation of Concerns
- Commands focus on state changes
- Queries focus on data retrieval
- Different optimization strategies for each
Scalability
- Can scale read and write operations independently
- Read queries can use denormalized views
- Write operations ensure consistency
Testability
- Handlers are simple, single-purpose classes
- Easy to unit test in isolation
- Clear dependencies through constructor injection
Maintainability
- Each use case is a separate class
- Easy to find and modify specific operations
- New features don’t impact existing code
Integration Patterns
Event-Driven Communication
Published Events:- Order created (via
OrderCreatedEventHandler) - Feature flag controlled
- Async publish via MassTransit
- BasketCheckoutEvent (via
BasketCheckoutEventHandler) - Triggers order creation
- Decoupled from Basket service
Transactional Outbox Pattern
Domain events are dispatched during the same transaction as database changes, ensuring:- Events are only published if database commit succeeds
- No lost events due to failures
- Consistency between state and events
Error Handling
Custom Exceptions
Validation Exceptions
FluentValidation throwsValidationException with detailed error messages.
Exception Handling Pipeline
- Validation exceptions return 400 Bad Request
- Not found exceptions return 404 Not Found
- Domain exceptions return 400 Bad Request
- Unhandled exceptions return 500 Internal Server Error
Best Practices
Command Handlers
- Keep thin, delegate to domain models
- Map DTOs to domain objects
- Let aggregates enforce business rules
- Return simple result objects
Query Handlers
- Use
AsNoTracking()for performance - Project to DTOs directly when possible
- Include related entities to avoid N+1 queries
- Consider caching for frequently accessed data
Event Handlers
- Keep fast and lightweight
- Don’t throw exceptions (events should be fire-and-forget)
- Log errors instead of propagating
- Consider eventual consistency
Next Steps
- Domain Model - Understand the domain entities and value objects
- API Reference - Explore the REST endpoints that trigger these commands and queries
