How DDD Transforms Complex Software: Practical Insights and Real‑World Refactoring
This article explores the fundamentals of Domain‑Driven Design, its challenges, practical steps for applying DDD concepts such as value objects, repository patterns, hexagonal architecture, and domain layer design, and shares concrete refactoring experiences to build more maintainable, extensible, and testable systems.
Introduction
Software systems are delivered to solve specific business problems, and software design bridges business domains and development. Domain‑Driven Design (DDD) offers a methodology for large, complex applications, aiming for high extensibility, maintainability, and testability across business, system, deployment, data, and engineering architectures.
Why DDD Is Hard to Adopt
DDD is a mindset rather than a concrete technology, so it imposes no direct constraints on code or architecture. Mature ORM frameworks (e.g., Hibernate, MyBatis) encourage database‑centric development, and traditional layered architectures resemble DDD layers, leading to misunderstandings that hinder practical adoption.
Key DDD Practices (Domin Primitive)
Make Implicit Concepts Explicit Example: Phone numbers consist of area code + number. Instead of scattering validation logic, create a PhoneNumber value object that encapsulates validation and area‑code extraction.
Make Implicit Context Explicit Example: In a transfer scenario, the amount "1000" hides the currency unit. Define a Money value object to represent both amount and currency, preventing bugs caused by missing context.
Encapsulate Multi‑Object Behavior Example: Currency conversion in cross‑border transfers can be wrapped in a value object that handles calculation and validation, simplifying the method body.
Difference Between DP and Value Object DP (proposed by Alibaba) extends the value‑object concept with validation, independent behavior, and immutability, emphasizing side‑effect‑free design.
DP vs. DTO Illustrated with a diagram (image omitted for brevity).
Application Architecture
Traditional MVC separates presentation, business logic, and data access. DDD‑based architecture splits the system into Application, Domain, and Infrastructure layers:
Application Layer : Coordinates UI and domain interactions; contains no business logic.
Domain Layer : Holds core business rules, entities, value objects, events, and domain services.
Infrastructure Layer : Provides technical capabilities (databases, MQ, gateways) without business logic.
Further evolution leads to a hexagonal (ports‑and‑adapters) architecture where the infrastructure becomes the outermost layer, improving dependency inversion.
Where to Place Utility and Configuration Code
Utilities (e.g., JSON parsers) belong to the infrastructure or a shared module, not the domain. Configuration classes are split: business‑related switches reside in the domain layer, while database or infrastructure settings belong to the infrastructure layer.
Repository Pattern
What Is a Repository? A repository abstracts data‑model access, operating on aggregate roots within the domain layer.
Why Use It? It decouples the domain from storage and mitigates the anemic model.
What Is the Anemic Model? Entities contain only data mapped to tables, with business logic scattered across services, helpers, and controllers, leading to poor maintainability, extensibility, and testability.
Typical symptoms include DTO‑like entities, duplicated validation, and widespread business logic outside the domain.
Converting Between Data and Domain Models
After introducing repositories, data and domain models are kept separate and converted via Assemblers or Converters. Mapping can be dynamic (BeanUtils, Dozer) or static (MapStruct), with static mapping offering better performance and type safety.
Repository Interface Guidelines
Use business‑oriented method names (e.g., save, find) instead of storage‑specific verbs.
Operate only on aggregate roots or entities, never on data‑model objects.
Domain Layer Design Guidelines
Entity Design Entities must maintain invariants; constructors should receive all required data or provide sensible defaults.
Factory Pattern Use factories to simplify complex entity creation while preserving consistency.
Avoid Public Setters Expose intent‑driven methods instead of generic setters to prevent inconsistent state.
Aggregate Roots Sub‑entities are accessed only through their aggregate root, which enforces consistency.
Limit Dependencies Entities should not depend directly on other aggregates or services; use IDs or method parameters to reference external objects.
Side‑Effect Management Entity behavior should affect only itself (and its children). Side effects are handled via domain events and an EventBus, though global singletons can hinder unit testing.
Domain Services
When a use case requires multiple domain objects and returns a value object, a domain service is appropriate. Types include:
Single‑object strategy services (operate on one entity but involve external rules).
Cross‑object transaction services (modify multiple entities atomically).
General component services (provide reusable behavior across entities).
Example code for Double Dispatch and a generic Movable interface is shown in the original article (code omitted for brevity).
Domain Policies (Strategy Objects)
Policies encapsulate stateless business rules with methods like canApply and a core operation, often used within domain services.
Handling Side Effects with Domain Events
Domain events propagate changes after an entity’s state mutation, using an EventBus. However, global singletons can impede testing, highlighting a current limitation.
Conclusion
Studying and applying DDD provides a solid design foundation for large, complex systems and offers a clear path for refactoring legacy “code‑mountain” projects into maintainable, extensible, and testable architectures.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
dbaplus Community
Enterprise-level professional community for Database, BigData, and AIOps. Daily original articles, weekly online tech talks, monthly offline salons, and quarterly XCOPS&DAMS conferences—delivered by industry experts.
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.
