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.

macrozheng
macrozheng
macrozheng
Master Domain-Driven Design: From Theory to a Real SpringBoot Project

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 layer

4.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.

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.

JavaDDDSpringBootDomain-Driven Design
macrozheng
Written by

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.

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.