"/>
侧边栏壁纸
博主头像
PySuper 博主等级

千里之行,始于足下

  • 累计撰写 272 篇文章
  • 累计创建 17 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

DDD 领域驱动设计

PySuper
2025-06-01 / 0 评论 / 0 点赞 / 22 阅读 / 0 字
温馨提示:
所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

1、什么是 DDD

  • 领域驱动设计(Domain-Driven Design,简称 DDD)是一种软件开发方法

  • 以业务领域为核心驱动软件开发,通过精准建模解决复杂业务问题,提升系统可维护性与扩展性

战略设计

  • 限界上下文(Bounded Context)

    • 业务概念的逻辑边界(如“订单”、“库存”),内部模型含义一致。

    • 不同上下文通过 上下文映射(Context Mapping)协作(如订单创建需调用库存检查)。

  • 子域(Subdomain)

    • 核心域:业务竞争力关键(如电商交易流程);

    • 支撑域:辅助核心域(如物流跟踪);

    • 通用域:跨系统复用功能(如用户认证)。

战术设计

组件

核心特性

典型示例

实体(Entity)

唯一标识 + 状态可变(ID不变)

用户(ID标识、姓名可变)

值对象(Value Object)

无标识 + 属性定义 + 不可变

地址(省市区组合)

领域服务(Domain Service)

处理跨对象复杂逻辑

计算订单总金额

仓储(Repository)

封装领域对象持久化,隔离数据存储技术细节

用户仓储接口(增删查改)

全链路请求处理

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、缺点

挑战

风险说明

学习曲线陡峭

需掌握限界上下文/实体等10+新概念

设计成本高

前期需深度业务分析,划分不当易导致重构

小型项目性价比低

分层规范可能延长开发周期(尤其需求多变场景)

5.3、注意事项

  • 业务深挖:与领域专家紧密协作,确保模型精准反映业务本质

  • 边界智慧:限界上下文划分忌过细(增加集成成本)或过粗(丧失解耦价值)

  • 规避过度设计:简单场景勿强套DDD全套路(如CRUD直接用传统模式)

  • 模型持续演进:建立模型迭代机制(例:每季度业务评审后更新领域对象)

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区