1. Introduction
As software systems grow in complexity, managing business rules, scalability, and maintainability becomes a challenge.
Traditional layered architectures often result in anemic models—where business logic gets scattered between controllers and services.
Domain-Driven Design (DDD) helps solve this by structuring your application around the business domain, making your code more maintainable and closer to real-world business processes.
In this article, we’ll learn how to design and build clean, scalable APIs using ASP.NET Core 9 with DDD principles, including:
Entities, Value Objects, Aggregates
Domain Services and Repositories
Infrastructure and Application Layers
Handling Commands and Events
Example Implementation and folder structure
2. What Is Domain-Driven Design?
Domain-Driven Design (DDD) is a software design approach introduced by Eric Evans.
It focuses on the core domain and domain logic rather than technical layers.
In DDD:
You model the real business directly in the code.
The domain model is the heart of the application.
The application grows from the business language, not just CRUD operations.
3. Core DDD Building Blocks
| Concept | Description |
|---|---|
| Entity | An object with an identity that persists over time (e.g., Customer, Order). |
| Value Object | Immutable object identified by its attributes (e.g., Money, Address). |
| Aggregate | A cluster of entities and value objects treated as a single unit. |
| Repository | Provides abstraction for retrieving and saving aggregates. |
| Domain Service | Encapsulates domain logic that doesn’t belong to a specific entity. |
| Application Service | Orchestrates operations, coordinates between domain objects. |
4. Architecture Overview
A clean DDD-based ASP.NET Core API typically has these layers:
┌────────────────────────────┐
│ Presentation │
│ (Controllers, DTOs, API) │
└────────────┬───────────────┘
│
┌────────────▼───────────────┐
│ Application Layer │
│ (Use Cases, Commands, DTOs) │
└────────────┬───────────────┘
│
┌────────────▼───────────────┐
│ Domain Layer │
│ (Entities, Services, Logic) │
└────────────┬───────────────┘
│
┌────────────▼───────────────┐
│ Infrastructure Layer │
│ (EF Core, DB, Repositories) │
└────────────────────────────┘
This separation ensures high cohesion within each layer and low coupling between layers.
5. Technical Workflow
Technical Workflow: Request to Domain
Client → API Controller → Application Service → Domain Layer (Entities/Logic)
↓
Infrastructure Repository (EF Core)
↓
Database
This ensures:
Controllers only handle HTTP.
Application services handle orchestration.
Domain entities handle core business logic.
6. Example Use Case: Order Management System
Let’s implement a small Order Management API using DDD.
Folder Structure
/OrderManagement
│
├── Application
│ ├── DTOs
│ ├── Services
│ └── Commands
│
├── Domain
│ ├── Entities
│ ├── ValueObjects
│ ├── Services
│ └── Repositories
│
├── Infrastructure
│ ├── Data
│ └── Repositories
│
└── API
├── Controllers
└── Startup.cs
7. Domain Layer
Entity: Order.cs
namespace OrderManagement.Domain.Entities
{
public class Order
{
public Guid Id { get; private set; }
public string CustomerName { get; private set; }
private readonly List<OrderItem> _items = new();
public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly();
public Order(string customerName)
{
Id = Guid.NewGuid();
CustomerName = customerName;
}
public void AddItem(string product, int quantity, decimal price)
{
var item = new OrderItem(product, quantity, price);
_items.Add(item);
}
public decimal GetTotalAmount()
{
return _items.Sum(x => x.Total);
}
}
}
Value Object: OrderItem.cs
namespace OrderManagement.Domain.Entities
{
public class OrderItem
{
public string Product { get; }
public int Quantity { get; }
public decimal Price { get; }
public decimal Total => Quantity * Price;
public OrderItem(string product, int quantity, decimal price)
{
Product = product;
Quantity = quantity;
Price = price;
}
}
}

Join the conversation! Your thoughts help the community grow.