Master Domain-Driven Design: From Theory to a Real SpringBoot Project
This article explains the fundamentals of Domain-Driven Design, compares it with traditional MVC, details DDD’s layered architecture, core concepts such as entities, value objects, aggregates, services and events, and walks through a complete SpringBoot demo project with code, directory layout, database schema and deployment steps.
1. Introduction to DDD
1.1 Why use DDD?
Object‑oriented design, bind data and behavior, avoid anemic models.
Reduce complexity by dividing problems.
Prioritize domain model over data‑centric design.
Accurately convey business rules.
Code is design.
Boundary separation simplifies complex domains and supports unified architecture evolution.
Share domain knowledge, improve collaboration.
Increase maintainability, readability and lifespan.
Foundation for a middle‑platform.
1.2 Role of DDD
In MVC three‑layer architecture, requirements are first turned into table structures, then DAO, service, controller, which often leads to business logic being scattered in the code layer.
When business boundaries are unclear, logic accumulates in the code, making maintenance hard.
Example: an e‑commerce order flow involves order, payment, shipping, etc.
MVC architecture : design tables first, then add tables and logic as features evolve.
DDD architecture : define business boundaries first; order becomes the aggregate, other concepts are attributes of the order entity.
Overall DDD benefits:
Eliminate information asymmetry.
Reverse bottom‑up design to top‑down, business‑driven.
Split large requirements into manageable parts.
2. DDD Architecture
2.1 Layered Architecture
Strict layering: a layer can only depend on the layer directly below it. Loose layering: upper layers may use any lower layer.
DDD adopts loose layering; each layer can use services of any lower layer.
User Interaction Layer : web, RPC, MQ requests that may modify internal data.
Application Layer : orchestrates, forwards, validates; differs from MVC service which contains business logic.
Domain Layer : core, expresses business concepts, rules, aggregates, services, events, factories.
Infrastructure Layer : provides persistence, messaging, utilities, configuration.
Avoid placing domain logic in the application layer to keep the domain model focused.
3. DDD Fundamentals
3.1 Domain and Sub‑Domain
DDD splits business problems into domains and further into sub‑domains, establishing clear boundaries for modeling.
3.2 Core, Generic and Supporting Domains
Core domain : determines product and company competitive advantage.
Generic domain : reusable functions used by multiple sub‑domains.
Supporting domain : neither core nor generic.
3.3 Ubiquitous Language and Bounded Context
Ubiquitous language : a clear, shared vocabulary for business concepts.
Bounded context : encapsulates the language and domain objects, ensuring unambiguous meaning.
3.4 Entity and Value Object
Entity = unique identifier + mutable state/behavior; represented as a DO (Domain Object).
Value object = immutable representation of a specific concept, without identity.
3.5 Aggregate and Aggregate Root
Aggregate groups strongly related entities and value objects, defining a consistency boundary. The aggregate root manages the aggregate and serves as the external entry point.
3.6 Domain Service and Application Service
Domain services contain logic that does not belong to a single entity. Application services orchestrate use cases, delegate to domain objects, and handle cross‑cutting concerns.
3.7 Domain Event
Domain events capture significant occurrences: publish, store, distribute, and handle.
3.8 Repository
Repositories abstract persistence for aggregates, allowing the domain layer to remain persistence‑agnostic.
4. DDD in Practice
4.1 Project Overview
Focus on user, role and their relationship to build a permission‑allocation model.
Four‑layer DDD architecture: interface, application, domain, infrastructure.
Data flow uses VO, DTO, DO, PO for layer isolation.
Implemented with SpringBoot, MyBatis‑Plus, MySQL.
4.2 Directory Structure
./ddd-application // application layer
./ddd-common // common utilities
./ddd-common-application // shared application module
./ddd-common-domain // shared domain module
./ddd-common-infra // shared infrastructure
./ddd-domain // domain layer
./ddd-infra // infrastructure layer
./ddd-interface // API layer4.3 Database
Three tables: t_user, t_role, t_user_role with typical fields and timestamps.
create table t_user (
id bigint auto_increment primary key,
user_name varchar(64) null comment '用户名',
password varchar(255) null comment '密码',
real_name varchar(64) null comment '真实姓名',
phone bigint null comment '手机号',
...
);4.4 Domain Layer Implementation
Domain objects (AuthorizeDO) contain userId, userName, realName, phone, password, unit, address, roles.
public class AuthorizeDO {
private Long userId;
private String userName;
private String realName;
private String phone;
private String password;
private UnitDTO unit;
private AddressDTO address;
private List<RoleDTO> roles;
}Domain service sets unit information (placeholder for third‑party call).
@Service
public class AuthorizeDomainServiceImpl implements AuthorizeDomainService {
@Override
public void associatedUnit(AuthorizeDO authorizeDO) {
String unitName = "武汉小米"; // TODO: fetch from third‑party
authorizeDO.getUnit().setUnitName(unitName);
}
}Domain events such as UserCreateEvent extend BaseDomainEvent and are published via Spring’s ApplicationEventPublisher.
public class UserCreateEvent extends BaseDomainEvent<AuthorizeDO> {
public UserCreateEvent(AuthorizeDO user) {
super(user);
}
}4.5 Application Layer
Application services orchestrate DTO‑DO conversion, invoke domain services, persist via repository, and publish events.
@Transactional(rollbackFor = Exception.class)
public void createUserAuthorize(UserRoleDTO userRoleDTO) {
AuthorizeDO authorizeDO = userApplicationConverter.toAuthorizeDo(userRoleDTO);
authorizeDomainService.associatedUnit(authorizeDO);
AuthorizeDO saved = userRepository.save(authorizeDO);
domainEventPublisher.publishEvent(new UserCreateEvent(saved));
}Query method retrieves data, converts to DTO, and returns.
public UserRoleDTO queryUserAuthorize(Long userId) {
AuthorizeDO authorizeDO = userRepository.query(userId);
if (Objects.isNull(authorizeDO)) {
throw ValidationException.of("UserId is not exist.", null);
}
return userApplicationConverter.toAuthorizeDTO(authorizeDO);
}4.6 Running the Project
Create database using init.sql.
Configure datasource in application.yml.
Start the SpringBoot application.
Test with POST /api/user/save and GET /api/user/query.
5. Conclusion
DDD is a methodology rather than a technology; it emphasizes deep domain modeling, ubiquitous language, and clear boundaries. Proper upfront design of domain objects and interactions is crucial for long‑term maintainability, especially for complex core services.
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.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
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.
