How Domain‑Driven Design Turns Complex Business Logic into Clear Code
This article explains why domain models are needed, walks through DDD's strategic and tactical design concepts, demonstrates a full knowledge‑payment platform case with bounded contexts, aggregates, and ubiquitous language, and outlines practical guidelines and limitations for applying DDD in real projects.
Why a Domain Model?
Traditional MVC works for simple use‑cases but quickly shows three problems as business complexity grows: the code lacks a shared business language, data and behavior are split across layers, and the system’s boundaries become fuzzy. A domain model solves these issues by providing:
Unified language – product, design and development teams use the same terminology.
Business‑technical decoupling – the domain model is independent from the persistence model, allowing the core business to evolve without data‑layer constraints.
Knowledge retention – the model encodes business rules, making them explicit and transferable.
DDD Core Concepts Overview
Strategic Design – System Partitioning
Strategic design answers “how to split the system”. The main concepts are:
Domain : the whole set of activities an organization performs (e.g., a knowledge‑payment platform).
Sub‑domain : an independent business section within the domain (e.g., Subscription, Column, Finance).
Core sub‑domain : the part that provides competitive advantage (e.g., Subscription).
Generic sub‑domain : reusable functions used by many sub‑domains (e.g., Authentication, Authorization).
Supporting sub‑domain : enterprise‑specific features that support other business areas (e.g., Comments, Columns).
Bounded Context : a clear business boundary that typically maps to a microservice (e.g., Column‑Subscription Context).
Tactical Design – Building the Model
Entity : an object with a unique identity and lifecycle (e.g., Subscription, Column).
Value Object : an immutable set of attributes without identity (e.g., Address, Money).
Aggregate : a cluster of entities and value objects that share a consistency boundary.
Aggregate Root : the single entry point to an aggregate.
Domain Service : business behavior that does not naturally belong to any entity.
Repository : abstraction over data‑access logic.
Domain Event : a significant occurrence inside the domain.
Practical Case – RabbitTech Knowledge‑Payment Platform
Business Scenario
Author creates a column.
User browses columns.
User pays for a subscription.
User reads content.
Company shares revenue with the author.
Domain Partition
Knowledge‑payment domain
├── Subscription (core) – subscription, order
├── Column (support) – column, article
├── Finance (support) – revenue share, settlement
├── User (generic) – user, permission
└── Comment (support) – comment, likeBounded Context Partition
┌─────────────────────────────────────┐
│ Column‑Subscription Context (MS1) │
│ Subscription ←── Order │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Column‑Management Context (MS2) │
│ Column ←── Article │
└─────────────────────────────────────┘Aggregate Design Example – Subscription
Subscription Aggregate
├── Subscription (Aggregate Root)
│ ├── id: SubscriptionId
│ ├── userId: UserId
│ ├── columnId: ColumnId
│ ├── status: SubscriptionStatus
│ └── subscribedAt: DateTime
├── SubscriptionItem (Entity)
│ └── ArticleReadRecord
└── Value Objects
├── SubscriptionPeriod
└── PaymentAmountUbiquitous Language – The Collaboration Foundation
Key Terms for “User subscribes to a column”
User : a person registered in the platform.
Column : a collection of content created by an author.
Subscription : a record that a user has paid for a column.
Order : the payment transaction that creates a subscription.
Applying the Language in Code
public class Subscription {
private SubscriptionId id;
private UserId userId;
private ColumnId columnId;
private SubscriptionStatus status;
private DateTime subscribedAt;
public void activate() {
if (this.status != SubscriptionStatus.PENDING) {
throw new IllegalStateException("Only pending subscriptions can be activated");
}
this.status = SubscriptionStatus.ACTIVE;
}
}Key Practices for DDD Implementation
Choosing an Entity Model
Four common model shapes are described below. The recommended approach for most DDD projects is the Passive (anemic) model , where entities contain the essential state and domain services hold the business logic.
Anemic Model : only fields and getters/setters – suitable for simple CRUD.
Passive Model : entities hold state; domain services implement behavior – DDD‑recommended.
Rich Model : entities encapsulate all business logic – used for very complex core domains.
Fat Model : mixes non‑business concerns – generally discouraged.
Aggregate Design Principles
Consistency boundary : all objects inside an aggregate must remain consistent.
Access through the root : external code may only reach members via the aggregate root.
Prefer small aggregates : smaller aggregates reduce concurrency conflicts.
Eventual consistency : aggregates communicate through domain events to achieve eventual consistency.
Bounded Context ↔ Microservice Mapping
One bounded context ≈ one microservice
Partitioning principles:
- Supports a complete business process
- Enables team autonomy
- Allows independent deploymentWhen DDD Is Appropriate
Complex business rules and frequent changes.
Large development teams with dedicated domain experts.
Products that evolve over time.
When DDD Is Not Recommended
Simple CRUD applications.
Rapid prototypes or short‑term projects.
Small teams where the focus is technology rather than business.
Summary
Strategic design defines how to split the system into bounded contexts.
Tactical design shows how to implement entities, value objects, aggregates and services.
Ubiquitous language ensures the whole team speaks the same terms.
Domain model captures and preserves business knowledge.
Code examples referenced in this article are available in the following repositories: Backend microservices: https://github.com/eyebluecn/smart-classroom-misc Frontend project: https://github.com/eyebluecn/smart-classroom-front
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
