Unlock DDD: Practical Hexagonal Architecture and Repository Patterns
This article explores Domain‑Driven Design fundamentals, illustrating how to expose hidden concepts, adopt hexagonal architecture, and implement repository patterns to achieve high scalability, maintainability, and testability in complex software systems, while addressing common pitfalls like the anemic model.
Software systems are delivered through development to solve specific business domains, and software design bridges business and development. Domain‑Driven Design (DDD) provides a mindset for designing large, complex software with high extensibility, maintainability, and testability.
Challenges of Applying DDD
DDD is a philosophy rather than a concrete technology, so there is no code‑level constraint. Because mature ORM frameworks (e.g., Hibernate, MyBatis) encourage direct database‑oriented development, many developers mistake DDD’s layered architecture for traditional layered architecture, leading to misunderstanding and difficulty in adoption.
1. Implicit Concepts Made Explicit
Example: A phone number consists of an area‑code and a number. Business logic often repeats validation and area‑code extraction across many methods. DDD suggests extracting the hidden concept "area‑code" into a value object
PhoneNumberthat encapsulates validation and extraction, eliminating repetitive code.
1.2 Implicit Context Made Explicit
Example: In a bank transfer, "1000" actually has two meanings: the numeric value and the currency unit (yuan). Ignoring the unit can cause bugs, especially for international transfers. DDD recommends creating a
Moneyvalue object to make the currency context explicit.
1.3 Encapsulating Multi‑Object Behavior
Example: In cross‑border transfers, exchange‑rate conversion can be encapsulated in a value object, simplifying the method by moving calculation and validation logic into the object.
1.4 DP vs. Value Object
DP (Domin Primitive) is a concept introduced by Alibaba, extending the DDD value object with additional capabilities such as validation, independent behavior, and immutability (no side effects).
1.5 DP vs. DTO
1.6 Using DP vs. Not Using DP
2. Application Architecture under DDD
2.1 Standard DDD Application Architecture
Traditional MVC separates presentation, business logic, and data access layers, focusing on top‑down interaction. DDD splits the system into Application Layer, Domain Layer, and Infrastructure Layer, placing core business logic in the Domain Layer.
2.1.1 Application Layer
Coordinates interaction between the user interface and the domain layer; essentially orchestrates domain services without containing business logic.
2.1.2 Domain Layer
Implements core business logic, including entities, value objects, events, and domain services.
2.1.3 Infrastructure Layer
Provides technical capabilities such as databases, scheduled tasks, message queues, and gateways; contains no business logic.
2.2 Understanding Hexagonal Architecture
2.2.1 Re‑examining the Application Layer
External systems (e.g., XXL‑Job, MQ, HTTP/RPC interfaces) also call the service, so the application layer must coordinate external calls with the domain layer. To preserve testability, the application layer should depend on interfaces (dependency inversion) and abstract external services.
2.2.2 Re‑examining the Domain Layer
The domain layer should not depend on infrastructure; using repository interfaces abstracts persistence, allowing the domain layer to remain independent.
2.2.3 Re‑examining the Infrastructure Layer
Infrastructure provides ports and adapters: ports define external interactions, adapters implement data and concept conversion.
2.2.4 Evolution to Hexagonal Architecture
By inverting dependencies, the infrastructure layer becomes the outermost layer.
2.3 Placement of Utility and Configuration Classes
Utility classes (JSON parsers, string helpers) are logically part of the infrastructure layer, but to avoid breaking dependency order they can be placed in a separate "common" module. Configuration classes should be placed according to their nature: business‑related switches belong to the domain layer, while database configurations belong to the infrastructure layer.
2.4 Practical Hexagonal Architecture in a Project
3. Repository Pattern
3.1 What Is a Repository?
In DDD, a repository abstracts the data store, handling aggregate roots and belonging to the domain layer.
3.2 Why Use a Repository?
It decouples the domain from storage and provides a standard way to avoid the anemic model.
3.3 What Is the Anemic Model?
Entities contain only data mapped to database tables with little or no behavior; business logic is scattered across services, utilities, and handlers.
3.4 Detecting an Anemic Model
Entities (e.g., XxxDO) contain only fields without behavior.
Business logic is dispersed in services, controllers, utilities, helpers, handlers, leading to duplicated code.
3.5 Why the Anemic Model Is Bad
Cannot guarantee entity invariants; state can be altered arbitrarily.
Boundary of entity behavior is unclear, making changes error‑prone.
Strong coupling to persistence; changes in the database force changes in business code.
Software maintainability = amount of code to change when infrastructure changes. Software extensibility = amount of code to change when business logic changes. Software testability = execution time of test cases × number of test cases affected by a change.
3.6 Why the Anemic Model Persists
Developers often conflate data models with domain models, adopt a database‑first mindset, prioritize rapid delivery, and fall into script‑style coding, all of which reinforce the anemic approach.
3.7 Converting Data Models and Domain Models
After introducing repositories, data models and domain models are kept separate and converted via Assembler or Converter components.
3.8 Mapping Solutions
Dynamic mapping (BeanUtils, Dozer) uses reflection and incurs performance overhead. Static mapping (MapStruct) generates compile‑time code with performance equal to hand‑written mappings.
4. Domain Layer Design Guidelines
4.1 Entity Classes
Entities encapsulate state and behavior within a domain. Key principles:
Creation Consistency: Constructors must receive all required attributes or provide sensible defaults.
Use Factory pattern to simplify complex construction.
Avoid public setters; expose intent‑driven methods instead.
Aggregate roots enforce consistency among child entities.
Do not depend directly on other aggregates or domain services.
Depend on other aggregates only via IDs or method parameters.
Entity behavior should affect only itself and its children.
Prefer enums over inheritance for simple variations; consider Type Object pattern for data‑driven designs.
4.2 Domain Services
Used when a business operation requires multiple domain objects and returns a value object.
4.2.1 Single‑Object Strategy Service
Handles rules involving a single entity plus external dependencies.
4.2.2 Cross‑Object Transaction Service
Ensures consistency when a behavior modifies multiple entities.
4.2.3 Generic Component Service Provides reusable behavior not tied to a specific entity. Double Dispatch Example <code>public void equip(Weapon weapon, EquipmentService equipmentService) { if (equipmentService.canEquip(this, weapon)) { this.weaponId = weapon.getId(); } }</code> 4.3 Strategy Objects (Domain Policy) Encapsulate domain rules as stateless singleton objects with methods like canApply and a business operation. 4.4 Handling Side Effects – Domain Events Side effects (e.g., awarding points) are modeled as domain events propagated via an EventBus. The EventBus is typically a global singleton, which can hinder unit testing. 5. Conclusion Studying and practicing DDD deepens understanding of software design as a discipline that guides the construction of large, complex systems and provides a clear path for refactoring legacy codebases.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
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.