Fundamentals 46 min read

Why a Proper Domain Model Is the Heart of Successful Software Design

This article explains the core principles of Domain‑Driven Design, emphasizing the creation of a correct domain model, the challenges of communication between experts, the layers of a DDD‑based architecture, and essential patterns such as entities, value objects, aggregates, factories, repositories, and CQRS.

ITFLY8 Architecture Home
ITFLY8 Architecture Home
ITFLY8 Architecture Home
Why a Proper Domain Model Is the Heart of Successful Software Design

In 2004 Eric Evans published Domain‑Driven Design – Tackling Complexity in the Heart of Software , often abbreviated as Evans DDD. DDD consists of two stages: using a common language understandable by domain experts, designers, and developers to discover domain concepts and model them, then driving software design with that domain model and implementing it in code.

The core of DDD is building the correct domain model.

DDD tells us that when implementing a business system, establishing a domain model is essential because a domain model has several characteristics:

A domain model is an abstraction of a bounded domain that reflects the essence of user business requirements; it is bounded and only reflects the part we care about within the domain.

The domain model reflects business only and is independent of any technical implementation; it can represent entities such as cargo, books, interview records, addresses, as well as processes like fund transfers.

All business logic resides in a single model, improving maintainability, understandability, and reusability.

The domain model helps developers translate domain knowledge into software construction smoothly.

The model is used throughout analysis, design, and development; experts, designers, and developers share knowledge through the model, preventing requirement drift and ensuring the software truly meets the needs.

Creating a correct model requires active communication among domain experts, designers, and developers.

To make the model visible, we use diagrams, code, or textual descriptions.

The domain model is the core of the software, the most valuable and competitive part; a well‑designed model enables rapid response to changing requirements.

Collaboration between software experts and domain experts is essential, but communication barriers often arise because developers think in classes, methods, algorithms, patterns, and architectures, while domain experts speak only their own terminology. Misunderstandings can jeopardize project success.

During communication, translation is needed so that everyone understands the concepts. Developers may try to explain design patterns in lay terms, but this is not always effective. Domain experts may also invent jargon, which does not help knowledge construction.

A core DDD principle is to use a model‑based language. Since the model is the common ground, the team must use a consistent language in all forms of communication—speech, text, and diagrams—known as the Ubiquitous Language . This language should be explored throughout modeling to discover the main domain concepts.

Having a correct model does not guarantee direct code conversion; implementation must still respect good design principles. Issues such as persistence and performance may arise, requiring careful handling.

Domain modeling and design should be tightly coupled; developers must participate in modeling so that the model can be expressed in code without violating design principles. Early feedback from developers helps ensure the model is implementable.

“User needs” are not the same as “users”; the model should be built around domain concepts, not centered on the user. An analogy: a cup must be empty before it can hold water; a house must be built empty before people can live in it. The model should accommodate user needs without being user‑centric.

Key takeaways:

Design the domain model from an objective perspective, focusing on domain concepts rather than user actions.

The model excludes people as primary elements, though it may include participant roles; keeping people out preserves objectivity.

Example from Evans’s cargo‑transport system:

A Cargo involves multiple Customer roles (shipper, consignee, payer).

The cargo has a designated destination.

Transport is achieved through a series of Carrier Movements that satisfy a Specification .

This description focuses on cargo and its relationships, not on user‑centric views.

Distinguishing “user” from “participant”:

In a football game, the operator (user) drives the actions of participants (players). The player may act as a mirror of another participant.

Presentation / UI Layer

Responsible for displaying information to users and interpreting user commands. Specifically:

Request the application layer for data to display.

Send commands to the application layer to execute user actions.

Application Layer

A thin layer that defines all tasks the software must perform. It provides application functionality to the presentation layer and invokes the domain layer (domain objects or services) to execute business logic. It contains no business logic itself.

Domain Layer

Expresses business concepts, state, and rules; the domain model resides here and is the core of the business software.

Infrastructure Layer

Provides technical capabilities to other layers, handles communication, and implements persistence for the domain layer. It supports the other layers through frameworks and infrastructure.

Overall Pattern Overview

Designing Associations

Associations are crucial in domain modeling. Principles for designing them include:

Keep associations minimal; complex webs hinder understanding and maintenance.

One‑to‑many relationships are natural but may need separate queries for large collections.

Prefer unidirectional associations.

Identify constraints on associations and encode them to simplify relationships (e.g., turning many‑to‑many into one‑to‑many).

Entity

An entity is a domain concept that requires a unique identifier. Different identifiers mean different entities even if other attributes match. Entities have lifecycles and may be persisted and later reloaded. Keep entities focused; extract related data into other entities or value objects when appropriate.

Value Object

A value object has no unique identifier; equality is based on all attribute values. Value objects are immutable, can be safely shared, and should be simple. In C# they typically override GetHashCode and Equals for value equality.

Domain Service

Some domain concepts are better expressed as operations rather than objects because they involve multiple domain objects. Domain services are stateless, named with verbs (e.g., MoneyTransferService), and coordinate objects without leaking domain logic into the application layer.

There are three kinds of services in typical software: application services, domain services, and infrastructure services.

Application Service

Receive input (e.g., an XML request).

Send a command to a domain service to perform business logic.

Upon success, invoke an infrastructure service to send an email notification.

Domain Service

Obtain source and target accounts, then coordinate debit and credit operations.

Return results to the application layer.

Infrastructure Service

Send email notifications based on application‑layer requests.

Thus each service’s responsibilities are clear.

Aggregate and Aggregate Root

An aggregate defines a cohesive group of related objects with clear boundaries, treated as a single unit of modification.

Each aggregate has a root entity and a boundary that defines which entities or value objects belong to it.

Objects inside the aggregate may reference each other, but external code must access them only through the aggregate root.

Only the root has a globally unique identifier; internal entities use local identifiers.

The aggregate root handles external interactions and enforces internal business rules.

Database queries should retrieve aggregates as whole units, not individual internal objects.

Internal objects may reference other aggregate roots.

Deleting an aggregate root must delete all its internal objects.

How to identify aggregates and roots:

First consider business significance: an object that has meaning independently of others may be a candidate for an aggregate root. If multiple entities exist, choose the one that can be accessed directly from outside.

How to Identify Aggregates

Analyze business relationships to find tightly‑coupled groups that should be treated as a single whole. Keep aggregates small to avoid performance issues; most aggregates contain a single root entity, and a few contain two or three entities.

How to Identify Aggregate Roots

If an aggregate has only one entity, that entity is the root. If there are multiple entities, select the one that has independent meaning and can interact with the outside world.

Factory

Factories encapsulate the creation of complex domain objects, especially aggregates. Clients provide simple parameters; the factory builds the object while enforcing business rules. If creation fails, an exception is thrown. Simple objects can be created directly via constructors.

Repository

Repositories exist because domain objects are persisted when not in memory. They provide collection‑like interfaces for retrieving, adding, or removing objects.

Repositories store aggregates, not individual entities, ensuring whole‑aggregate retrieval and persistence.

Repositories define interfaces in the domain layer; the infrastructure layer implements them, allowing the application layer to work with abstractions.

Repositories typically do not handle transactions; a Unit of Work does.

Query interfaces may use the Specification pattern for flexible criteria.

Steps for building a domain model include identifying concepts, defining aggregates, creating repositories, and refining the model.

Domain modeling is an iterative process of refinement, driven by collaboration among domain experts, designers, and developers.

Interaction Between Layers

In the classic DDD layered architecture, the domain layer sits between the application layer (above) and the infrastructure layer (below). The domain layer interacts with other layers via services and repositories.

Application‑Layer Functions That Affect Domain State

Typical workflow:

Retrieve a domain object via a repository and invoke its business methods to modify state.

Create a new domain object via constructor or factory, optionally perform additional operations, then add it to a repository.

Delete a domain object by retrieving it and removing it from the repository, or by passing its identifier to the repository.

When a business operation involves multiple objects, invoke the appropriate domain service.

All domain objects accessed are aggregate roots, and repositories and services are obtained through an IoC container. Finally, the Unit of Work commits the transaction, persisting changes.

Unit of Work Implementations

Snapshot‑based: objects are cloned when retrieved; on commit, the current state is compared to the snapshot to detect changes.

Non‑snapshot, repository‑driven: repositories notify the Unit of Work when objects are added, updated, or deleted.

Non‑snapshot, AOP‑driven: transparent proxies intercept property changes and inform the Unit of Work (e.g., NHibernate).

INotifyPropertyChanged implementation (C#): objects raise property‑changed events to signal modifications.

Query‑Only Functions

Queries can be performed directly via repositories. When more complex views are needed, a separate query engine (CQRS) can be used, bypassing the domain layer to improve performance.

Object‑oriented design helps contain changes within bounded contexts, reducing the impact of bugs and simplifying maintenance.

Additional DDD Topics (Not Covered in Detail)

Bounded contexts, context mapping, and shared kernels.

Applying analysis and design patterns within DDD.

Techniques for flexible design.

Maintaining model integrity and continuous integration.

Refining the model, identifying core and generic subdomains.

These topics are important but omitted for brevity.

CQRS Architecture

The core idea is to separate the query side from the command side, allowing each to use different models and technologies. Commands can be implemented with DDD, while queries may use direct SQL for maximum performance.

Command models are not compromised by query requirements.

Different technologies and databases can be chosen for each side.

Commands can be queued for asynchronous processing, improving scalability.

Event Sourcing

Instead of persisting the current state of an aggregate, each event that changes the aggregate is stored. To rebuild an aggregate, replay its events; snapshots can be used to improve performance.

DCI Architecture

DCI (Data, Context, Interaction) models software after real‑world interactions. Data models correspond to DDD domain models; role models define behaviors for identities; contexts represent use‑case scenarios that assign roles to data objects, enabling interaction.

Domain services in DDD and roles in DCI achieve similar coordination, but DCI emphasizes explicit interaction between objects.

Four‑Color Archetype Analysis

Moment‑Interval Archetype (MI)

Represents an activity occurring at a specific moment or over a time interval; shown in pink.

Part‑Place‑Thing Archetype (PPT)

Represents participants, locations, and items involved in an activity; shown in green.

Description Archetype (DESC)

Provides an invariant, essential description of a PPT; shown in blue.

Role Archetype (Role)

Denotes the identity or role required to participate in an activity; shown in yellow.

The four‑color archetype can be summarized as: a person/organization/item with a certain description (DESC), playing a role (Role), participates in an activity at a moment or interval (MI).

Understanding these archetypes deepens DDD knowledge, but DDD itself remains the foundational step.

Hope this article helps you.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Aggregatesrepositories
ITFLY8 Architecture Home
Written by

ITFLY8 Architecture Home

ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.