Posted by Firuza Polad on June 28, 2025
Domain Driven Design (DDD)
DDD is not a design pattern, not an architecture pattern/style, not a
technology, coding technique, it is a software development approach that indicate close
collaboration between development team and business people. We apply DDD when we have BL
complexity and application features changes often. We don't apply when we have simple
domain,
CRUD based application, 20-30 use cases. Key Aspects:
- - Centralized BS logic : core idea of DDD focus on Business domain itself, this
ensure that software main purpose is solving business problems.
-
- Learn, discuss, bring BS value : continuous communication between them,
developers and
BS people help to each other to understand logic well, and achieve better design, where
minimize gaps. Everyone use same language (UL) when discussing in system.
-
- Design is code, code is design : there is no strict separation between design
and
code. Design should be reflected in code, and code need represent the business design.
-
-Separation of Concerns
: DDD encourage seperating Domain logic from Application/Infrastructure/UI
logic. Often implemented with
Clean Architecture or Onion Architecture. Note that, Clean and Onion architecture is a
software architecture pattern , while DDD is
a design and modeling approach.
-
- Strategic/Tactical designs: in DDD it brings both, Strategic and Tactical
designs together.
1. Strategic design in DDD focus on higher level design that are help
splitting the domain into manageable pieces.
-
~ Subdomain is a business concept, we use it in Problem space. Simply, we can
say it's a spesific part of business domain. Let’s say we are building an online
bookstore that are selling
books online, this is a whole business domain, but this domain has many parts which are
callled subdomains, such as Catalog,Ordering,Billing,UserManagment etc. Each Subdomain
is implemented by one or more Bounded Contexts, such as Catalog with
CatalogBoundedContext,
Ordering with OrderManagementBoundedContext etc.
There are 3 types of domain:
- a. Core Subdomain: is most important, unique part of business. Require deep
collaboration with domain
experts.
- b. Supporting Subdomain: is not unique, used to reuse patterns or tools if
possible. Eg; User managment.
- c. Generic Subdomain: is commonly used logic that every company needs. We don't
build it unless necessary.
Instead using existing frameworks/libraries etc. Eg; Authentication &
Authorization, Logging, Payment gateways etc.
- ~ Ubiquitous Language(UL) is a shared language used by both the development team
and
business
people
to ensure clear communication. It is created by developers, domain expers and business
experts. s. The goal is to define key terms that everyone agrees on and to visually
represent domain elements and their relationships using diagrams or models. Terms like
“Book,”
“Order,” “Customer,” “Discount” are defined and used between business and
developers. The UL is only valid on inside a given BC. Each BC has its own
UL,
and this language can't use across in other contexts, this ensure that language remains
clear for
given context.
-
~ Bounded Context (BC) is a solution space where we can define and apply domain
models
independently. It isolates part of system to solve a specific part of larger problem. In
practice, a BC often used with Microservices architecture, where each microservices has
its own
isolated context, responsibility, and often its own DB and architecture style. As
earlier I mentioned, the UL is only valid with its
BC, each BC has its own UL, even if multiple BC use similar terminology — like an
"Account" in the Billing Context and in the UserManagement Context — the meaning of the
term may be completely different within each other. Tactical pattern are implemented in
BC. And each BC handle its
own solution. BC related to Solution space not Problem space.
-
~ Context Map (CM) provide high level overview to indicate how different BC are
related to each other. This is crucial when mapping out integration relationships
between multiple BC. There are some relationships between different part of system,
ensuring they can work
together without conflict, like "Partnership"- two BC are dependent on each other and
must either success or fail
together
-
~ Shared Kernel is set of common domain models (enties, value objects, servcies)
that are
shared between two or more BC. It allows context to share some core business logic
without
completely merging their domains. Eg: in E-commerce app, two teams are working on two
Bounded Context: OrderManagment and CustomerService, but both context need to use
Customer data (Customer entity, Address value object), instead of each team defining
their
own version of Customer, they share same domain model of Customer and Address. So this
part is Shared Kernel. Since multiple context depend on Shared Kernel, changes to it
require careful coordination between teams to avoid breaking other parts of the system.
2. Tactical design in DDD focus on code-level construct, such as value
object, entity, aggregate (root) ,model, repositories, domain services and application
services etc.
This pattern is used to implement a Domain Model in a BC. To apply
Tactical Patterns effectively, we should : first define BC, then create UL, and ensure clear
solution space communication using CM, then
we can apply Tactical Pattern.
-
~ Entity: is a class that represent business concept with unique identifier even
if their
attribute has same name. This
is not an Anemic model - a class
only consist of properties and logic declared somewhere else (in a service layer), It is
anti-pattern in DDD, violtes rules.
Entity class tied
to Domain.
a. Identified by its unique ID: It differentiate one entity from another;
b. Has a lifecycle: can be created,modified,deleted;
c. Mutable and stateful: represented by its attributes, which can change over time
Two entities are only considered equal, if they have the same
identity, but usually a unique identifier assigned to an entity when it’s created. We
use Equals() and GetHashCode() for equality check.
- Equals() - defined in System.Object class and inherited by all
objects in C#. Works for both value types and reference types: For value types
checks the content/value. For reference types it behaves like
ReferenceEqual() method, means check if both references point to the same
object. But unlike ReferenceEqual(), we can
override Equals() for
reference types.
Note: == and Equals() both check content for Value
types,
but for Reference types we can override == by impelenting operator
overloading. When
we override Equals()
we must also override GetHashCode(), if we override == and != then we should also
override Equals() and GetHashCode() to keep behavior.
- GetHashCode() - provide hash code (int) for an object, used in hash-based
collections
(Dictionaries, HashSet) for quickly find and group objects that might be equal. If
two objects
are equal based on Equals() then they must return the same hash code.
-
~ Model : Is a simple data representation, may or may not have ID, typically used
outside of strict DDD boundariers. Often used to holding data only, not behavior.
Stateless and not responsible for BS rules. Can be Mutable and Immutable based on its
use case. Tied to ViewModel , DTO, InputModel and used to carry data between layers.
-
~ Value object (VO): It is a concept in the domain, doesn't have an
Identity, described by its attributes. If two value objects are equal then all their
properties are equal. To prevent duplicates, we need logic to compare all fields. We
create class named ValueObject and
override Equals(), GetHashCode(). Without it, Sets or LINQ won't behaves as excepted. It
is Immutable,
means once created, then can not be changed later,
instead create new instance.
Has no lifecycle, commonly used in Entity.
-
~ Aggregate & AggregateRoot(AR) : Aggregate is designed to encapsulate a group of
related Enties and Value objects as single unit. Aggregate can contain multiple enties,
but one entity/value object
can be part of one aggregate. All objects are tied up together in aggregate.
Aggregate Root (AR) is primary entity in Aggregate and control access to other entities.
Because
the other entities are private, they can be accessed via AR. Repositories typically
works with AR.
-
~ Repositories : Is abstraction over DAL layer and provide access to Aggregate.
Act like a collection, provide methods Add(), Remove(), Update(), GetById() and return
domain objects specifically Entities or Aggregate Roots, not DTO or Model. The Domain
layer
doesn't know where data comes from - DB, FIle, External service. Repository operate on
AR only, not on internal entities and Interface declared in Domain layer , but
implementation is provided in Infrastructure layer-ORM (EF,Dapper,Raw SQL).
-
~ Domain services : Is used to encapsulate domain logic that doesn't belong to a
single Entity. Contain BS rules and logic that are still part of domain
model, but not tied to a spesific object. It is stateless, does not store data, only
coordinates logic. Eg; CanPlaceOrder() encapsulate
business logic if Customer can place Order. There are two enties, Customer and Order,
but
logic doesn't belong for either Customer or Order.
NOTE: in here we don't use this method inside Entity, because Entity are
handle
logic belongs to their behavior, like change state of order, apply discount.
-
~ Application services : Coordinates tasks in the application, such as calling
domain services, interacting with repositories, handling transactions, logging, and
communicating with external systems (UI, DB, APIs). In a nutshell, it handles the
workflow of the application. Unlike Domain Services, it contains no business logic.
-
~ Domain event : Something happens in domain that is important for BS logic. Once
a
domain event occurs, its state can not be changed. It typically describe something has
already happened in the system, help keep different parts of application loosely coupled
by allowing them to react to event without tight dependencies. Ex: when an order is
placed, that could be Domain Event, because it may trigger other actions like sending a
confirmation email, notify something.
-
~ Modules : We can think as folder that group similar concepts together. Eg:
Order Module have Order.cs, OrderPlacedEvent.cs etc.
-
~ Factories : is responsible for creating complex objects (like Aggregates), help
to
ensure that obj are created follow the correct BS rules. Eg: we have Order aggregate
that requires complex logic, like applying discount, and we return an Entity or
Aggregate back in valid state.
Solution Space vs Problem Space:
Solution space- is where we start define architecture (Microservices, Clean
Architecture,
CQRS) and technology
(programming languages, frameworks, databases) that will be solve problems which is defined
in Problem space, like BC
is solution space, in here we implement Entities, Value Objects, Domain Services.
Problem space- explore and define BS problem, in here we focus on what business needs
to
achieve, what is workflow, and define the UL. It is often called
Subdomain (distinct areas of the BS problem). First we define the Problem space by defining
a UL, and then we map subdomains into one or more BC in Solution space. Eg; we define Book
subdomain - what a Book means in business term, what workflow it has. Then we create Book
BC-implement Book entity, reposiotry, domain service to solve BS problem.