Domain-Driven Design
Application created with Flexbase Domain-Driven Design Patterns & Best Practices
ποΈ Domain-Driven Design Implementation
The Application created with Flexbase demonstrates sophisticated Domain-Driven Design (DDD) patterns through its Handler-Domain-Domain Method architecture, showcasing enterprise-grade best practices and clean separation of concerns.
π― Core DDD Architecture
Three-Layer Domain Pattern
βββββββββββββββββββββββββββββββββββββββ
β Handler Layer (Application) β β Orchestrates business operations
βββββββββββββββββββββββββββββββββββββββ€
β Domain Layer (Business Logic) β β Contains pure business logic
βββββββββββββββββββββββββββββββββββββββ€
β Infrastructure Layer (Data) β β Handles persistence and external concerns
βββββββββββββββββββββββββββββββββββββββ
Key DDD Principles Implemented
Rich Domain Models - Business logic encapsulated in domain entities
Ubiquitous Language - Domain terms used consistently across all layers
Aggregate Roots - Domain entities manage their own state and invariants
Domain Events - Business events published for side effects
Repository Pattern - Clean data access abstraction
π§ Handler Layer - Application Orchestration
Handler Responsibilities
The Handler layer acts as the Application Service in DDD, orchestrating business operations without containing business logic.
// Handler Pattern - Application Service
public partial class AddOrderHandler : IAddOrderHandler
{
// Dependencies injected - Clean Architecture
protected readonly ILogger<AddOrderHandler> _logger;
protected readonly IFlexHost _flexHost; // Domain access
protected readonly RepoFactory _repoFactory; // Data access
// Domain model instance
protected Order? _model;
protected FlexAppContextBridge? _flexAppContext; // Context
public virtual async Task Execute(AddOrderCommand cmd, IFlexServiceBusContext serviceBusContext)
{
// 1. Context Extraction - DDD Context Management
_flexAppContext = cmd.Dto.GetAppContext();
// 2. Repository Initialization - Infrastructure Setup
_repoFactory.Init(cmd.Dto);
// 3. Domain Method Invocation - CORE DDD PATTERN
_model = _flexHost.GetDomainModel<Order>().AddOrder(cmd);
// 4. Persistence Layer - Infrastructure Concern
_repoFactory.GetRepo().InsertOrUpdate(_model);
int records = await _repoFactory.GetRepo().SaveAsync();
// 5. Event Publishing - DDD Event-Driven Architecture
await this.Fire(EventCondition, serviceBusContext);
}
}
Handler Best Practices Demonstrated
1. Single Responsibility Principle
Only orchestrates - No business logic in handlers
Manages dependencies - Coordinates between domain and infrastructure
Handles transactions - Ensures data consistency
Publishes events - Triggers side effects
2. Dependency Inversion Principle
Depends on abstractions - Uses interfaces, not concrete implementations
Domain-first approach - Handler calls domain, not vice versa
Infrastructure abstraction - Repository pattern for data access
3. Command Pattern Implementation
Encapsulates requests - Commands contain all necessary data
Type safety - Strongly-typed command parameters
Validation - Command-level validation before domain processing
ποΈ Domain Layer - Business Logic Encapsulation
Domain Model Structure
Domain models are Aggregate Roots that encapsulate business logic and maintain invariants.
// Domain Model - Aggregate Root
public partial class Order : DomainModelBridge
{
// Domain Properties - Business State
public Guid Id { get; set; }
public string CustomerId { get; set; }
public decimal TotalAmount { get; set; }
public string Status { get; set; }
public DateTime CreatedDate { get; set; }
// Navigation Properties - Domain Relationships
public virtual Customer? Customer { get; set; }
public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
// Audit Properties - Cross-cutting Concerns
public string? CreatedBy { get; set; }
public string? LastModifiedBy { get; set; }
public DateTime? LastModifiedDate { get; set; }
}
Domain Model Best Practices
1. Rich Domain Models
Behavior over data - Methods contain business logic
Encapsulation - Internal state protected from external modification
Invariants - Business rules enforced within the domain
2. Aggregate Root Pattern
Single entry point - All operations go through the aggregate root
Consistency boundaries - Ensures data integrity within the aggregate
Transaction scope - One aggregate per transaction
3. Value Objects and Entities
Entities - Have identity (Order, Customer)
Value Objects - No identity, immutable (Money, Address)
Clear boundaries - Well-defined domain concepts
βοΈ Domain Methods - Business Logic Implementation
Domain Method Pattern
Domain methods contain the core business logic and are the heart of DDD implementation.
// Domain Method - Business Logic Implementation
public virtual Order AddOrder(AddOrderCommand cmd)
{
// 1. Input Validation - Domain Invariants
Guard.AgainstNull("Order command cannot be empty", cmd);
// 2. DTO to Domain Mapping - Data Transformation
this.Convert(cmd.Dto);
// 3. Business Logic Application - Core DDD
this.CreatedBy = cmd.Dto.GetAppContext()?.UserId;
this.LastModifiedBy = cmd.Dto.GetAppContext()?.UserId;
// 4. Domain State Management - Aggregate State
this.SetAdded(cmd.Dto.GetGeneratedId());
// 5. Business Rules Enforcement - Domain Invariants
this.ValidateOrderTotal();
this.ApplyBusinessRules();
// 6. Domain Event Publishing - Event-Driven DDD
this.RaiseEvent(new OrderCreatedEvent
{
OrderId = this.Id,
CustomerId = this.CustomerId,
TotalAmount = this.TotalAmount,
CreatedAt = DateTime.UtcNow
});
return this;
}
Domain Method Best Practices
1. Business Logic Encapsulation
Pure business logic - No infrastructure concerns
Domain invariants - Business rules enforced
Validation - Domain-level validation
State management - Aggregate state changes
2. Command Pattern Integration
Command objects - Encapsulate all necessary data
Type safety - Strongly-typed parameters
Context preservation - User and request context maintained
3. Event-Driven Architecture
Domain events - Business events published
Side effects - Triggered by domain events
Loose coupling - Modules communicate through events
π Complete DDD Flow Example
AddOrder Operation - DDD in Action
graph TB
subgraph "Handler Layer (Application)"
Handler[AddOrderHandler.Execute]
Context[Extract Context]
Repo[Initialize Repository]
Persist[Persist Changes]
Event[Publish Events]
end
subgraph "Domain Layer (Business Logic)"
Domain[Order Domain Model]
Method[AddOrder Method]
Rules[Business Rules]
Validation[Domain Validation]
State[State Management]
end
subgraph "Infrastructure Layer (Data)"
Repository[Repository Factory]
Database[(Database)]
MessageBus[Event Bus]
end
Handler --> Context
Context --> Repo
Repo --> Domain
Domain --> Method
Method --> Rules
Method --> Validation
Method --> State
Method --> Event
Handler --> Persist
Persist --> Repository
Repository --> Database
Event --> MessageBus
Step-by-Step DDD Implementation
Step 1: Handler Orchestration
// Handler coordinates the operation
public virtual async Task Execute(AddOrderCommand cmd, IFlexServiceBusContext serviceBusContext)
{
// Extract application context
_flexAppContext = cmd.Dto.GetAppContext();
// Initialize infrastructure
_repoFactory.Init(cmd.Dto);
// Delegate to domain - CORE DDD PRINCIPLE
_model = _flexHost.GetDomainModel<Order>().AddOrder(cmd);
// Handle infrastructure concerns
_repoFactory.GetRepo().InsertOrUpdate(_model);
await _repoFactory.GetRepo().SaveAsync();
// Publish domain events
await this.Fire(EventCondition, serviceBusContext);
}
Step 2: Domain Business Logic
// Domain contains the business logic
public virtual Order AddOrder(AddOrderCommand cmd)
{
// Business validation
Guard.AgainstNull("Order command cannot be empty", cmd);
// Data transformation
this.Convert(cmd.Dto);
// Business rules application
this.CreatedBy = cmd.Dto.GetAppContext()?.UserId;
this.LastModifiedBy = cmd.Dto.GetAppContext()?.UserId;
this.SetAdded(cmd.Dto.GetGeneratedId());
// Domain event publishing
this.RaiseEvent(new OrderCreatedEvent { ... });
return this;
}
Step 3: Infrastructure Handling
// Infrastructure handles data persistence
_repoFactory.GetRepo().InsertOrUpdate(_model);
int records = await _repoFactory.GetRepo().SaveAsync();
// Event publishing for side effects
await this.Fire(EventCondition, serviceBusContext);
π― DDD Best Practices Demonstrated
1. Separation of Concerns
Handler Layer: Orchestration, transaction management, event publishing
Domain Layer: Business logic, rules, invariants, domain events
Infrastructure Layer: Data persistence, external service integration
2. Dependency Inversion
High-level modules (Handlers) don't depend on low-level modules (Database)
Domain layer is independent of infrastructure
Abstractions define contracts between layers
3. Single Responsibility Principle
Handlers: Only orchestrate operations
Domain Models: Only contain business logic
Repositories: Only handle data access
4. Open/Closed Principle
Open for extension: New domain methods, handlers, events
Closed for modification: Existing domain logic doesn't change
Plugin architecture: Easy to add new business capabilities
5. Interface Segregation
Focused interfaces: Each interface serves specific purpose
Client-specific contracts: Handlers only depend on what they need
Loose coupling: Changes in one interface don't affect others
π Advanced DDD Patterns
1. Aggregate Pattern
// Order is an Aggregate Root
public partial class Order : DomainModelBridge
{
// Manages OrderItems as part of the aggregate
public virtual ICollection<OrderItem> OrderItems { get; set; }
// Business method that maintains aggregate consistency
public virtual Order AddItemToOrder(AddItemToOrderCommand cmd)
{
// Business logic to add item
// Maintains aggregate invariants
// Publishes domain events
return this;
}
}
2. Domain Events Pattern
// Domain events for side effects
public class OrderCreatedEvent : IFlexEvent
{
public Guid OrderId { get; set; }
public string CustomerId { get; set; }
public decimal TotalAmount { get; set; }
public DateTime CreatedAt { get; set; }
}
// Event publishing in domain method
this.RaiseEvent(new OrderCreatedEvent
{
OrderId = this.Id,
CustomerId = this.CustomerId,
TotalAmount = this.TotalAmount,
CreatedAt = DateTime.UtcNow
});
3. Repository Pattern
// Repository abstraction for data access
_repoFactory.GetRepo().InsertOrUpdate(_model);
int records = await _repoFactory.GetRepo().SaveAsync();
// Domain doesn't know about database specifics
// Infrastructure handles the implementation details
4. Command Pattern
// Commands encapsulate business operations
public class AddOrderCommand : FlexCommandBridge<AddOrderDto, FlexAppContextBridge>
{
public AddOrderDto Dto { get; set; }
public FlexAppContextBridge Context { get; set; }
public string CorrelationId { get; set; }
public DateTime Timestamp { get; set; }
}
π DDD Benefits Achieved
1. Maintainability
Clear boundaries - Each layer has distinct responsibilities
Business logic isolation - Domain logic is separate from infrastructure
Easy testing - Each layer can be tested independently
Consistent patterns - Same structure across all modules
2. Scalability
Horizontal scaling - Stateless handlers can be scaled independently
Event-driven - Loose coupling through domain events
Microservices ready - Clear boundaries enable service extraction
3. Business Alignment
Ubiquitous language - Domain terms used consistently
Business logic centralization - All business rules in one place
Domain expert collaboration - Clear domain model for business discussions
4. Quality Assurance
Type safety - Compile-time validation throughout
Invariant enforcement - Business rules enforced at domain level
Error prevention - Framework prevents common mistakes
Consistent behavior - Same patterns across all operations
π― Key Takeaways
DDD Implementation Success
The YourApplication solution demonstrates enterprise-grade DDD implementation with:
Clean Architecture - Clear separation between layers
Rich Domain Models - Business logic properly encapsulated
Event-Driven Design - Loose coupling through domain events
Repository Pattern - Clean data access abstraction
Command Pattern - Encapsulated business operations
Business Value Delivered
Maintainable Code - Clear structure and separation of concerns
Testable Architecture - Each layer can be tested independently
Scalable Design - Ready for horizontal scaling and microservices
Business Alignment - Domain model reflects business concepts
Quality Assurance - Consistent patterns and error prevention
This document showcases how the YourApplication solution implements Domain-Driven Design principles through its Handler-Domain-Domain Method architecture, delivering enterprise-grade maintainability, scalability, and business alignment.
Last updated