Sunday, November 2, 2025

Architecture - Domain Driven Design (DDD)

Domain-Driven Design (DDD) is a software design approach that focuses on building software around the core business domain and its logic — not just the technology.

The goal is to create a system that reflects the real-world business rules, language, and processes of the domain you’re modeling.




Core Principles

PrincipleDescription
Domain-CentricBusiness logic is the heart of the application.
Ubiquitous LanguageDevelopers and domain experts use the same language to describe system behavior.
Bounded ContextsThe system is divided into smaller, focused domains that clearly define boundaries.
Entities & Value ObjectsModel domain concepts with clear identity and value semantics.
AggregatesGroup entities and value objects that change together.
RepositoriesAbstract data access and provide persistence logic.
Domain EventsRepresent significant events that happen in the domain.

Layers in Domain-Driven Design

DDD typically aligns closely with Clean Architecture / Onion Architecture.

Here’s how the layers look:

--------------------------------------- | Presentation Layer | | (Controllers, APIs, GraphQL, etc.) | --------------------------------------- | Application Layer | | (Use Cases, Commands, Handlers) | --------------------------------------- | Domain Layer | | (Entities, Value Objects, Services) | --------------------------------------- | Infrastructure Layer | | (Database, Repositories, AWS, etc.) | ---------------------------------------

Example in .NET

Let’s consider a simple Order Management domain.

1. Domain Layer

Entities and Value Objects:

public class Order { public Guid Id { get; private set; } public List<OrderItem> Items { get; private set; } = new(); public decimal Total => Items.Sum(i => i.TotalPrice); public void AddItem(string product, int quantity, decimal price) { Items.Add(new OrderItem(product, quantity, price)); } } public class OrderItem { public string Product { get; } public int Quantity { get; } public decimal UnitPrice { get; } public decimal TotalPrice => Quantity * UnitPrice; public OrderItem(string product, int quantity, decimal unitPrice) { Product = product; Quantity = quantity; UnitPrice = unitPrice; } }

2. Application Layer

Use Case / Command Handler:

public class CreateOrderHandler { private readonly IOrderRepository _repository; public CreateOrderHandler(IOrderRepository repository) { _repository = repository; } public async Task Handle(CreateOrderCommand command) { var order = new Order(); foreach (var item in command.Items) { order.AddItem(item.Product, item.Quantity, item.UnitPrice); } await _repository.AddAsync(order); } }

3. Infrastructure Layer

Repository Implementation (e.g., EF Core):

public class OrderRepository : IOrderRepository { private readonly AppDbContext _context; public OrderRepository(AppDbContext context) { _context = context; } public async Task AddAsync(Order order) { await _context.Orders.AddAsync(order); await _context.SaveChangesAsync(); } }

4. Presentation Layer

Controller:

[ApiController] [Route("api/orders")] public class OrdersController : ControllerBase { private readonly CreateOrderHandler _handler; public OrdersController(CreateOrderHandler handler) { _handler = handler; } [HttpPost] public async Task<IActionResult> Create(CreateOrderCommand command) { await _handler.Handle(command); return Ok(); } }

Applying DDD in .NET — Practical Steps

  1. Start with the Domain

    • Collaborate with business experts to define key entities, events, and value objects.

  2. Define Bounded Contexts

    • Split large systems into subdomains (e.g., Ordering, Inventory, Billing).

  3. Use Clean Architecture Principles

    • Keep your domain pure (no EF Core, no HTTP).

  4. Apply Dependency Inversion

    • Infrastructure depends on the domain, not the other way around.

  5. Leverage .NET Features

    • Dependency Injection (IServiceCollection)

    • EF Core for persistence

    • MediatR for use case orchestration

    • FluentValidation for business rules


Example in AWS Context

A DDD-based .NET project on AWS could look like this:

  • API Gateway → exposes endpoints

  • Lambda / ECS / EKS → hosts the application

  • RDS (PostgreSQL / SQL Server) → stores domain data

  • SQS / SNS → handles domain events and messaging

  • CloudWatch → logs domain events and metrics


Key Benefits

BenefitDescription
MaintainabilityChanges in business logic isolated in domain layer
TestabilityPure domain logic easy to test
FlexibilityInfrastructure and UI can change independently
Business AlignmentCode mirrors business concepts and rules

In Summary

Domain-Driven Design (DDD) is an architectural and modeling approach that builds software around business logic.
In .NET, it integrates naturally with Clean Architecture and Dependency Injection, creating systems that are modular, testable, and business-focused.

No comments:

Post a Comment

CI/CD - Safe DB Changes/Migrations

Safe DB Migrations means updating your database schema without breaking the running application and without downtime . In real systems (A...