← Back to Articles
.NET C# Domain Driven Design Clean Architecture Microservices Software Architecture

Domain Driven Design in .NET: Complete Guide with C# Examples, Clean Architecture, and Enterprise Patterns

Learn how to apply Domain Driven Design (DDD) in .NET applications. Understand entities, value objects, aggregates, domain events, bounded contexts, and enterprise architecture patterns with practical C# examples.

July 2026
30 min read
Written by Engineering Team

Introduction

As software systems grow, technical complexity is rarely the biggest challenge. The real difficulty comes from understanding complex business rules and transforming them into reliable software.

Domain Driven Design (DDD) is an approach to software development that focuses on building applications around business concepts rather than technical details.

In the .NET ecosystem, DDD is commonly used for enterprise applications, financial systems, healthcare platforms, e-commerce solutions, and large microservice architectures.

Domain Driven Design architecture overview
DDD connects business concepts with software architecture.

What Is Domain Driven Design?

Domain Driven Design was introduced by Eric Evans as an approach for creating software that closely represents the business domain it solves.

Instead of starting with database tables, frameworks, or technical infrastructure, DDD starts with understanding business processes, rules, and terminology.

  • Focus on business domain

  • Create shared language between developers and experts

  • Model real business rules

  • Separate complex business logic

  • Create maintainable architectures

Why Use DDD in .NET Applications

Many traditional applications place business logic inside controllers, services, or database layers. Over time, this creates systems that are difficult to understand and modify.

DDD provides a structured approach where business rules become part of the domain model.

Traditional Approach DDD Approach
Database first design Business first design
Anemic models Rich domain models
Logic in services Logic inside domain objects
Technical language Business language

The Two Main Areas of DDD

Domain Driven Design consists of two major areas: strategic design and tactical design.

Area Focus
Strategic Design How the system is divided
Tactical Design How domain objects are implemented

Strategic Design: Understanding the Business Domain

Strategic design focuses on understanding the business and creating boundaries between different parts of the system.

  • Bounded Contexts

  • Ubiquitous Language

  • Context Mapping

  • Domain Subdomains

Ubiquitous Language

Ubiquitous Language means developers and business experts use the same terminology when discussing the system.

A common problem in software projects is that developers use technical terms while business users use completely different words.

OrderExample.cs
csharp
              public class Order
{
    public OrderStatus Status { get; private set; }


    public void Confirm()
    {
        Status = OrderStatus.Confirmed;
    }
}

            

The code should represent real business concepts such as ConfirmOrder, CancelOrder, or ApprovePayment instead of generic database operations.

Bounded Contexts

A Bounded Context defines a clear boundary where a specific domain model applies.

Large systems often contain different meanings for the same concept. DDD avoids forcing everything into one global model.

DDD bounded contexts example
Different bounded contexts can have different models of the same business concept.
  • Sales Context

  • Inventory Context

  • Shipping Context

  • Billing Context

Tactical Design Concepts

Tactical design describes the building blocks used to implement the domain model in code.

  • Entities

  • Value Objects

  • Aggregates

  • Aggregate Roots

  • Domain Events

  • Domain Services

  • Repositories

Entities in Domain Driven Design

Entities are objects that have a unique identity and continue to exist even when their properties change.

The identity of an entity is more important than its attributes.

Customer.cs
csharp
              public class Customer
{
    public Guid Id { get; private set; }

    public string Name { get; private set; }


    public void ChangeName(
        string name)
    {
        Name = name;
    }
}

            

Value Objects

Value Objects represent concepts defined by their values instead of identity.

Two value objects with identical values are considered equal.

Money.cs
csharp
              public record Money(
    decimal Amount,
    string Currency);

            
  • Money

  • Address

  • Email

  • Coordinates

  • Date Range

Aggregates in Domain Driven Design

Aggregates are one of the most important concepts in Domain Driven Design. An aggregate is a group of related domain objects that are treated as a single unit for consistency and business rules.

Instead of allowing every object to modify data independently, aggregates define clear boundaries where business rules are protected.

Every aggregate has one entry point called the Aggregate Root.

DDD aggregate structure
An aggregate protects business rules through its aggregate root.

Aggregate Root

The Aggregate Root is the main entity responsible for controlling access to objects inside an aggregate.

External code should never directly modify internal entities. All changes should go through the aggregate root.

OrderAggregate.cs
csharp
              public class Order
{
    private readonly List<OrderItem> items =
        new();


    public IReadOnlyCollection<OrderItem> Items =>
        items;


    public void AddItem(
        Product product,
        int quantity)
    {
        if(quantity <= 0)
            throw new Exception(
                "Invalid quantity");


        items.Add(
            new OrderItem(
                product,
                quantity));
    }
}

            

The Order aggregate controls how items are added and prevents invalid states.

Aggregate Design Rules

  • Keep aggregates small

  • Protect business invariants

  • Change aggregates through the root

  • Avoid references between aggregates

  • Use IDs instead of direct object references

💡 DDD Tip

Large aggregates create unnecessary complexity and reduce scalability. Design aggregates around business consistency boundaries.

Domain Events

Domain Events represent something important that happened inside the business domain.

Events are written in past tense because they describe completed actions.

  • OrderCreated

  • PaymentCompleted

  • CustomerRegistered

  • InvoiceGenerated

OrderCreatedEvent.cs
csharp
              public record OrderCreatedEvent(
    Guid OrderId,
    DateTime CreatedAt);

            

Publishing Domain Events in .NET

In .NET applications, domain events are commonly handled using mediator libraries, message brokers, or internal event dispatchers.

DomainEventHandler.cs
csharp
              public class OrderCreatedHandler
{
    public async Task Handle(
        OrderCreatedEvent notification)
    {
        await SendEmail();

        await UpdateStatistics();
    }
}

            
  • Decouple business operations

  • Enable event-driven architecture

  • Support microservices communication

  • Improve maintainability

Domain Services

Domain Services contain business logic that does not naturally belong to a single entity or value object.

A domain service should contain pure business operations and should not depend on infrastructure concerns.

CurrencyExchangeService.cs
csharp
              public interface ICurrencyExchangeService
{
    Money Convert(
        Money amount,
        string currency);
}

            

Currency conversion is a business rule but does not belong naturally inside the Money value object.

Repositories in Domain Driven Design

Repositories provide a collection-like abstraction for accessing aggregates.

The domain layer should not know whether data comes from SQL Server, PostgreSQL, MongoDB, or another storage system.

IOrderRepository.cs
csharp
              public interface IOrderRepository
{
    Task<Order?> GetAsync(
        Guid id);


    Task SaveAsync(
        Order order);
}

            

Domain Driven Design with Entity Framework Core

Entity Framework Core can work very well with DDD when the database model is treated as an implementation detail.

A common mistake is allowing EF Core entities to become simple database containers without business behavior.

OrderConfiguration.cs
csharp
              public class OrderConfiguration
    : IEntityTypeConfiguration<Order>
{
    public void Configure(
        EntityTypeBuilder<Order> builder)
    {
        builder.HasKey(
            x => x.Id);


        builder.OwnsMany(
            x => x.Items);
    }
}

            

Rich Domain Model vs Anemic Model

An anemic domain model contains only properties and moves all business logic into services.

A rich domain model keeps business rules close to the data they protect.

Anemic Model Rich Domain Model
Objects only store data Objects contain behavior
Logic in services Logic in entities
Weak validation Protected invariants
Harder maintenance Clear business rules

DDD Layers with Clean Architecture

Domain Driven Design works naturally with Clean Architecture because both approaches focus on separating business logic from technical infrastructure.

DDD clean architecture layers
Typical Clean Architecture structure with DDD.
  • Domain Layer

  • Application Layer

  • Infrastructure Layer

  • Presentation Layer

Domain Layer

The Domain Layer contains the core business rules and should have no dependency on databases, APIs, frameworks, or external services.

  • Entities

  • Value Objects

  • Aggregates

  • Domain Events

  • Domain Services

Application Layer

The Application Layer coordinates use cases. It connects domain objects with external systems but does not contain core business rules.

CreateOrderHandler.cs
csharp
              public class CreateOrderHandler
{
    private readonly IOrderRepository repository;


    public async Task Handle()
    {
        var order = new Order();

        await repository.SaveAsync(order);
    }
}

            

Domain Driven Design in Microservices Architecture

Domain Driven Design is one of the most important architectural approaches when building microservices because it helps identify service boundaries based on business capabilities instead of technical layers.

A common mistake when creating microservices is splitting applications by database tables or technical components. DDD recommends creating boundaries around business domains.

DDD microservices bounded contexts
Microservices aligned with bounded contexts.
  • Each bounded context can become an independent service

  • Each service owns its business logic

  • Services can have separate databases

  • Teams can work independently

Bounded Contexts in Microservices

A bounded context defines the responsibility and language of a specific part of the business.

For example, an e-commerce platform may contain multiple bounded contexts.

Bounded Context Responsibility
Catalog Products and categories
Ordering Customer purchases
Payment Transactions and invoices
Shipping Delivery process

Each context has its own model. The meaning of Product in Catalog may be different from Product in Ordering.

DDD and CQRS

CQRS (Command Query Responsibility Segregation) works naturally with Domain Driven Design by separating operations that change state from operations that read data.

Commands modify the domain model while queries return optimized read models.

CreateOrderCommand.cs
csharp
              public record CreateOrderCommand(
    Guid CustomerId,
    List<OrderItemDto> Items);

            
  • Complex business workflows

  • High traffic applications

  • Independent scaling

  • Event-driven architectures

DDD and Event Sourcing

Event Sourcing stores changes as a sequence of domain events instead of storing only the current state.

The current state of an aggregate can be rebuilt by replaying previous events.

OrderEvents.txt
text
              OrderCreated

PaymentCompleted

OrderShipped

OrderDelivered

            
  • Complete audit history

  • Event replay capability

  • Better debugging

  • Supports complex workflows

Integration Events

Domain Events are internal to a bounded context, while Integration Events communicate between different systems or microservices.

IntegrationEvent.cs
csharp
              public record OrderCompletedIntegrationEvent(
    Guid OrderId,
    DateTime CompletedAt);

            

Integration events are commonly published through message brokers such as RabbitMQ, Kafka, or Azure Service Bus.

DDD with Message Brokers

Large distributed systems often combine DDD with asynchronous messaging to communicate between bounded contexts.

DDD message broker architecture
Bounded contexts communicating through events.
  • Order service publishes OrderCreated

  • Payment service processes payment

  • Inventory service reserves products

  • Shipping service prepares delivery

Common Domain Driven Design Mistakes

  • Creating entities without behavior

  • Using one giant domain model

  • Making aggregates too large

  • Treating DDD as only folder structure

  • Mixing infrastructure with domain logic

  • Creating unnecessary abstractions

"DDD is not about adding more classes. It is about creating software that reflects the business."

DDD vs Traditional Layered Architecture

Traditional Architecture DDD Architecture
Organized by technical layers Organized by business domains
Database-driven design Business-driven design
Services contain most logic Domain objects contain behavior
Shared models Bounded contexts

When Should You Use Domain Driven Design?

DDD is most valuable when applications contain complex business rules and long-term development requirements.

  • Enterprise applications

  • Financial systems

  • Healthcare platforms

  • Large e-commerce systems

  • Complex SaaS products

  • Microservice architectures

For simple CRUD applications, introducing full DDD may create unnecessary complexity.

DDD Implementation Checklist

  • Understand the business domain

  • Define bounded contexts

  • Create ubiquitous language

  • Model entities and value objects

  • Protect aggregates

  • Use domain events

  • Separate infrastructure concerns

  • Apply SOLID principles

Frequently Asked Questions

Is Domain Driven Design only for large applications?
DDD provides the most value in complex systems, but smaller applications can also benefit from selected concepts such as entities, value objects, and domain services.
Is DDD the same as Clean Architecture?
No. DDD focuses on modeling business domains, while Clean Architecture focuses on dependency direction and application structure. They work well together.
Does DDD require microservices?
No. DDD can be used in monolithic applications. Microservices often use DDD because bounded contexts help define service boundaries.
Should every entity have a repository?
No. Repositories usually belong to aggregate roots, not every individual entity.
Can Entity Framework Core be used with DDD?
Yes. EF Core supports private fields, value objects, owned entities, and custom mappings required for DDD implementations.

Conclusion

Domain Driven Design helps .NET developers create software that represents real business processes instead of only technical structures. By using bounded contexts, entities, value objects, aggregates, and domain events, applications become easier to maintain, test, and evolve as business requirements grow.

We use cookies to improve your experience.