1、什么是 DDD
领域驱动设计(Domain-Driven Design,简称 DDD)是一种软件开发方法
以业务领域为核心驱动软件开发,通过精准建模解决复杂业务问题,提升系统可维护性与扩展性
战略设计
限界上下文(Bounded Context)
业务概念的逻辑边界(如“订单”、“库存”),内部模型含义一致。
不同上下文通过 上下文映射(Context Mapping)协作(如订单创建需调用库存检查)。
子域(Subdomain)
核心域:业务竞争力关键(如电商交易流程);
支撑域:辅助核心域(如物流跟踪);
通用域:跨系统复用功能(如用户认证)。
战术设计
全链路请求处理
2、为什么要使用 DDD
解耦复杂业务:通过领域模型分解庞杂业务逻辑,实现结构化抽象,显著降低认知复杂度。
提升可维护性:业务变更时精准定位修改点;限界上下文隔离模块变更影响,降低维护成本
增强可扩展性:模块化架构支持在子域边界内独立扩展(如新增促销功能),避免跨模块耦合
优化团队协作:统一领域语言(Ubiquitous Language)和明确上下文边界,大幅减少跨团队沟通成本
确保业务技术对齐:领域模型作为业务规则的核心载体,保障代码实现与业务需求高度一致
3、SpringBoot DDD
3.1、目录结构
传统 Spring Boot 项目一般以功能模块或业务类型划分包结构,为了适配DDD:
实现业务逻辑与技术分离
提高项目的可维护性和扩展性
调整后的目录结构如下:
yourcompany/
├── yourproject/
│ ├── application/ # 应用层,主要处理应用逻辑和用户请求的协调,包括应用服务、命令、查询等
│ │ ├── service/ # 应用服务,负责协调多个领域服务的调用
│ │ ├── command/ # 应用层的命令对象
│ │ └── query/ # 应用层的查询对象
│ ├── domain/ # 领域层,包含核心的业务逻辑和领域模型
│ │ ├── model/ # 存放领域模型,按聚合根、实体、值对象进行分层
│ │ │ ├── aggregates/ # 聚合根
│ │ │ ├── entities/ # 实体
│ │ │ └── valueobjects/ # 值对象
│ │ ├── services/ # 领域服务,处理跨聚合的复杂业务逻辑
│ │ ├── events/ # 领域事件,用于在领域内或跨领域传递变化
│ │ └── repository/ # 仓储接口,用于定义聚合的持久化操作
│ ├── infrastructure/ # 基础设施层,包含与技术实现相关的细节,如数据库、消息队列等
│ │ ├── repository/ # 仓储实现,通常使用Spring Data JPA等持久化框架
│ │ ├── persistence/ # 数据访问和持久化相关实现
│ │ ├── messaging/ # 处理与消息队列相关的事件传播
│ │ └── configuration/ # 系统配置,如数据库、消息系统等的配置类
│ ├── interfaces/ # 接口层,负责与外部交互,包括Web API、前端界面
│ │ ├── controller/ # 控制器,处理REST API或Web请求
│ │ ├── dto/ # 数据传输对象,用于在应用层和接口层之间传递数据
│ │ └── converter/ # DTO与领域对象之间的转换逻辑
│ └── config/ # 全局配置类
├── resources/ # 资源文件夹,存放配置文件和国际化文件等
│ ├── application.yml # Spring Boot配置文件
│ └── messages/ # 国际化资源文件
└── test/ # 测试代码
└── java/
└── resources/
3.2、各层实现
3.2.1、领域层
实体(Entity)
在 Spring Boot 项目中,实体通过 Java 类实现,通常会包含唯一标识符和业务相关的属性及方法。
以下是一个用户实体的示例:
public class User {
private Long id;
private String username;
private String password;
private Address address;
public User(Long id, String username, String password, Address address) {
this.id = id;
this.username = username;
this.password = password;
this.address = address;
}
// Getter and Setter methods
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
// 业务方法示例
public boolean validatePassword(String inputPassword) {
return password.equals(inputPassword);
}
}
值对象(Value Object)
值对象用于描述领域中的属性或状态,是不可变的。
java使用final关键字和仅包含getter方法来保证其不可变性,以下是地址值对象的示例:
public final class Address {
private final String province;
private final String city;
private final String district;
private final String street;
public Address(String province, String city, String district, String street) {
this.province = province;
this.city = city;
this.district = district;
this.street = street;
}
// Getter methods
public String getProvince() {
return province;
}
public String getCity() {
return city;
}
public String getDistrict() {
return district;
}
public String getStreet() {
return street;
}
}
领域服务(Domain Service)
领域服务用于处理复杂的业务逻辑,这些逻辑无法简单归类到实体或值对象中。
例如,用户注册的领域服务示例如下:
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User registerUser(User user) {
// 可以添加密码加密等业务逻辑
return userRepository.save(user);
}
public User findUserById(Long id) {
return userRepository.findById(id);
}
}
仓储(Repository)
仓储接口定义了对领域对象的持久化操作方法,在 Spring Boot 中,通常会使用 Spring Data JPA 等技术来实现具体的仓储
以下是用户仓储接口和基于 Spring Data JPA 的实现类示例:java
// 用户仓储接口
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository {
User save(User user);
User findById(Long id);
List<User> findAll();
}
// 用户仓储实现类(基于Spring Data JPA)
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepositoryImpl extends JpaRepository<User, Long>, UserRepository {
// Spring Data JPA会自动实现接口中的方法,无需额外编写大量SQL代码
}
3.2.2、应用层
应用层接收外部的命令或查询请求,协调领域层的操作来完成业务流程。以下是用户注册的命令和应用服务示例:
// 创建用户命令
public class CreateUserCommand {
private String username;
private String password;
private Address address;
public CreateUserCommand(String username, String password, Address address) {
this.username = username;
this.password = password;
this.address = address;
}
// Getter methods
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public Address getAddress() {
return address;
}
}
// 应用服务
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ApplicationService {
private final UserService userService;
public ApplicationService(UserService userService) {
this.userService = userService;
}
@Transactional
public User handleCreateUserCommand(CreateUserCommand command) {
User user = new User(null, command.getUsername(), command.getPassword(), command.getAddress());
return userService.registerUser(user);
}
}
3.2.3、基础设施层
仓储实现
在 Spring Boot 中,除了使用 Spring Data JPA 实现仓储外,还可以使用其他数据访问技术。
以下是一个使用传统 JDBC 的用户仓储实现示例,用于展示不同技术下仓储的实现方式:
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class UserRepositoryJdbcImpl implements UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepositoryJdbcImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public User save(User user) {
String sql = "INSERT INTO users (username, password, province, city, district, street) VALUES (?,?,?,?,?,?)";
jdbcTemplate.update(sql, user.getUsername(), user.getPassword(), user.getAddress().getProvince(),
user.getAddress().getCity(), user.getAddress().getDistrict(), user.getAddress().getStreet());
// 假设数据库自增ID,这里获取最新插入的ID
Long newId = jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Long.class);
user.setId(newId);
return user;
}
@Override
public User findById(Long id) {
String sql = "SELECT id, username, password, province, city, district, street FROM users WHERE id =?";
return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);
}
@Override
public List<User> findAll() {
String sql = "SELECT id, username, password, province, city, district, street FROM users";
return jdbcTemplate.query(sql, new UserRowMapper());
}
private static class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id");
String username = rs.getString("username");
String password = rs.getString("password");
String province = rs.getString("province");
String city = rs.getString("city");
String district = rs.getString("district");
String street = rs.getString("street");
Address address = new Address(province, city, district, street);
return new User(id, username, password, address);
}
}
}
框架集成
在infrastructure/config目录下,通过配置类来实现与 Spring Boot 框架的集成
java例如数据源配置和 Spring 相关配置:
// 数据源配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
// Spring配置
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.example.myproject")
public class SpringConfig {
// 可以添加更多Spring相关的配置
}
3.2.4、接口层
Web 接口
在 Spring Boot 中,通过 Controller 类来处理 Web 请求。
以下是用户注册和查询的 Web 接口示例:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
private final ApplicationService applicationService;
public UserController(ApplicationService applicationService) {
this.applicationService = applicationService;
}
@PostMapping
public User registerUser(@RequestBody CreateUserCommand command) {
return applicationService.handleCreateUserCommand(command);
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
// 这里可以进一步调用领域服务获取用户
return null;
}
}java
API 接口
API 接口同样通过 Controller 类实现,用于提供 RESTful 风格的服务。
以下是一个简单的 API 接口示例:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/users")
public class UserApi {
@GetMapping
public String getUsers() {
return "This is a user API";
}
}java
4、Django DDD
将领域驱动设计(DDD)应用到 Django 项目中,能够有效应对复杂业务逻辑,提升项目的可维护性和扩展性。
4.1、目录结构
传统的 Django 项目目录结构主要围绕应用(app)展开,为了适配 DDD,需要对其进行重新组织,引入 DDD 的分层概念。
调整后的目录结构如下:
my_django_ddd_project
├── my_django_ddd_project
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── domain
│ ├── entities
│ │ ├── user.py
│ │ └── order.py
│ ├── valueobjects
│ │ ├── address.py
│ │ └── money.py
│ ├── services
│ │ ├── user_service.py
│ │ └── order_service.py
│ └── repositories
│ ├── user_repository.py
│ └── order_repository.py
├── application
│ ├── commands
│ │ ├── create_user_command.py
│ │ └── place_order_command.py
│ ├── queries
│ │ ├── get_user_query.py
│ │ └── get_order_query.py
│ └── application_service.py
├── infrastructure
│ ├── persistence
│ │ ├── user_repository_impl.py
│ │ └── order_repository_impl.py
│ ├── utils
│ │ ├── database_util.py
│ │ └── message_queue_util.py
│ └── django_integration
│ ├── signals.py
│ └── middleware.py
├── interfaces
│ ├── web
│ │ ├── views.py
│ │ ├── urls.py
│ │ └── templates
│ └── api
│ ├── serializers.py
│ ├── viewsets.py
│ └── urls.py
├── manage.py
└── tests
├── domain
│ ├── test_entities.py
│ ├── test_services.py
│ └── test_repositories.py
├── application
│ ├── test_commands.py
│ ├── test_queries.py
│ └── test_application_service.py
└── interfaces
├── test_web_views.py
└── test_api_viewsets.py
domain 目录:存放领域模型相关代码,是 DDD 的核心层,与 Django 框架解耦,专注于业务逻辑。
application 目录:协调领域层操作,处理应用级业务流程,如事务管理、权限控制等。
infrastructure 目录:提供技术支持,包括数据持久化实现、与 Django 框架集成的相关代码(如信号、中间件)。
interfaces 目录:负责与外部交互,分为 Web 接口(处理 HTTP 请求响应、渲染模板)和 API 接口(提供 RESTful API 服务)。
tests 目录:按分层结构组织测试代码,对各层功能进行单元测试和集成测试。
4.2、各层实现
4.2.1、领域层
实体(Entity)
在 Django 中,实体可以通过 Python 类实现,与 Django 的模型(Model)有所区别,领域实体更关注业务逻辑,不依赖 Django 的数据库特性。例如用户实体:
# domain/valueobjects/address.py
from dataclasses import dataclass
@dataclass(frozen=True)
class Address:
province: str
city: str
district: str
street: str
值对象(Value Object)
使用dataclass装饰器创建不可变的值对象,如地址值对象:
# domain/valueobjects/address.py
from dataclasses import dataclass
@dataclass(frozen=True)
class Address:
province: str
city: str
district: str
street: str
领域服务(Domain Service)
领域服务处理复杂业务逻辑,例如用户注册的领域服务:
# domain/services/user_service.py
from domain.entities.user import User
from domain.repositories.user_repository import UserRepository
class UserService:
def __init__(self, user_repository: UserRepository):
self.user_repository = user_repository
def register_user(self, username, password, address):
new_user = User(None, username, password, address)
self.user_repository.save(new_user)
return new_user
仓储(Repository)
定义仓储接口,规定对领域对象的持久化操作方法。例如用户仓储接口:
# domain/repositories/user_repository.py
from abc import ABC, abstractmethod
from domain.entities.user import User
class UserRepository(ABC):
@abstractmethod
def save(self, user: User) -> User:
pass
@abstractmethod
def find_by_id(self, id: int) -> User:
pass
@abstractmethod
def find_all(self) -> list[User]:
pass
4.2.2、应用层
应用层接收外部指令或查询,协调领域层操作。例如用户注册的命令和应用服务:
# application/commands/create_user_command.py
class CreateUserCommand:
def __init__(self, username, password, address):
self.username = username
self.password = password
self.address = address
# application/application_service.py
from domain.services.user_service import UserService
from application.commands.create_user_command import CreateUserCommand
class ApplicationService:
def __init__(self, user_service: UserService):
self.user_service = user_service
def handle_create_user_command(self, command: CreateUserCommand):
return self.user_service.register_user(command.username, command.password, command.address)
4.2.3、基础设施层
仓储实现
基于 Django 的模型和数据库操作实现仓储接口。例如基于 Django ORM 实现的用户仓储:
# infrastructure/persistence/user_repository_impl.py
from django.db import models
from domain.entities.user import User
from domain.repositories.user_repository import UserRepository
from infrastructure.persistence.models import UserModel # 自定义的Django模型
class UserRepositoryImpl(UserRepository):
def save(self, user: User) -> User:
user_model = UserModel(
username=user.username,
password=user.password,
address=user.address
)
user_model.save()
user.id = user_model.id
return user
def find_by_id(self, id: int) -> User:
try:
user_model = UserModel.objects.get(id=id)
return User(
id=user_model.id,
username=user_model.username,
password=user_model.password,
address=user_model.address
)
except UserModel.DoesNotExist:
return None
def find_all(self) -> list[User]:
user_models = UserModel.objects.all()
return [User(
id=user_model.id,
username=user_model.username,
password=user_model.password,
address=user_model.address
) for user_model in user_models]
框架集成
通过信号(Signals)和中间件(Middleware)实现与 Django 框架的集成。例如在用户注册成功后发送通知的信号:
# infrastructure/django_integration/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from infrastructure.persistence.models import UserModel
from domain.services.user_service import UserService
@receiver(post_save, sender=UserModel)
def send_user_register_notification(sender, instance, created, **kwargs):
if created:
user_service = UserService(None) # 需完善仓储注入
user = user_service.find_by_id(instance.id)
# 发送通知逻辑
4.2.4、接口层
Web 接口
在 Django 的视图(View)中调用应用层服务,处理 HTTP 请求并返回响应。例如用户注册的 Web 视图:
# interfaces/web/views.py
from django.shortcuts import render, redirect
from application.application_service import ApplicationService
from application.commands.create_user_command import CreateUserCommand
from domain.services.user_service import UserService
from infrastructure.persistence.user_repository_impl import UserRepositoryImpl
def register(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
address = request.POST.get('address')
user_repository = UserRepositoryImpl(None) # 需完善数据库连接
user_service = UserService(user_repository)
app_service = ApplicationService(user_service)
command = CreateUserCommand(username, password, address)
app_service.handle_create_user_command(command)
return redirect('home')
return render(request,'register.html')
API 接口
使用 Django REST framework 创建 API 接口,通过序列化器(Serializer)将领域对象转换为 JSON 数据。
例如用户 API 的序列化器和视图集:
# interfaces/api/serializers.py
from rest_framework import serializers
from domain.entities.user import User
class UserSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
username = serializers.CharField(max_length=100)
password = serializers.CharField(max_length=100)
address = serializers.CharField(max_length=200)
def create(self, validated_data):
return User(**validated_data)
# interfaces/api/viewsets.py
from rest_framework import viewsets
from domain.entities.user import User
from interfaces.api.serializers import UserSerializer
from application.application_service import ApplicationService
from application.commands.create_user_command import CreateUserCommand
from domain.services.user_service import UserService
from infrastructure.persistence.user_repository_impl import UserRepositoryImpl
class UserViewSet(viewsets.ModelViewSet):
queryset = [] # 需动态获取
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
address = request.data.get('address')
user_repository = UserRepositoryImpl(None) # 需完善数据库连接
user_service = UserService(user_repository)
app_service = ApplicationService(user_service)
command = CreateUserCommand(username, password, address)
user = app_service.handle_create_user_command(command)
serializer = self.get_serializer(user)
return Response(serializer.data)
5、优缺点 & 注意事项
5.1、优点
5.2、缺点
5.3、注意事项
业务深挖:与领域专家紧密协作,确保模型精准反映业务本质
边界智慧:限界上下文划分忌过细(增加集成成本)或过粗(丧失解耦价值)
规避过度设计:简单场景勿强套DDD全套路(如CRUD直接用传统模式)
模型持续演进:建立模型迭代机制(例:每季度业务评审后更新领域对象)
评论区