🌟 系列博客说明:
本文是 DDD“理论→Demo→实战” 三部曲的Demo 落地篇,基于 MVC 架构改造场景;前文《领域驱动设计(DDD)全链路实践指南:从理论到落地的体系化方案》(理论核心篇)已搭建完整 DDD 知识框架;后文《智慧园区架构演进:DDD + 三大思想破解循环依赖实战》(实战落地篇)将进一步展示 DDD 在真实复杂项目中的应用,帮助读者从 “会用” 到 “活用”。
引言:
在当今快速变化的业务环境中,传统的MVC架构逐渐暴露出业务逻辑分散、贫血模型、可测试性差等局限性。本文基于实际项目经验,系统阐述了如何将DDD从理论概念落地到工程实践,通过智慧园区系统案例,详细展示从战略设计到战术实现的完整流程。本文将揭示DDD不是架构师的专属工具,而是开发者回归业务本质的必备利器。
一、核心思想:面向开发者而非架构师
DDD工程化的本质是让领域驱动设计回归到日常开发实践,而不是停留在理论层面。其核心包括:
1.1 从数据库思维到领域思维
传统MVC架构以数据库表设计为出发点,导致业务逻辑分散在各个Service层。DDD工程化的本质是让开发回归业务本质,通过领域模型承载业务逻辑,实现技术架构与业务架构的统一。
1.2 业务边界优先原则
DDD的核心价值在于以业务边界作为架构设计的第一原则,通过限界上下文明确划分不同业务域的职责。
二、MVC架构问题诊断
2.1 传统开发模式的局限性
传统开发模式呈现出明显的线性流程:基于数据库表设计 → 绘制UML图 → 业务逻辑组装
这种模式导致以下核心问题:
1. 业务逻辑分散在各个Service层
- 核心业务流程被拆分到多个Service方法中
- 缺乏统一的业务规则管理机制
- 代码复用性差,相似逻辑在不同Service中重复实现
2. 实体对象成为贫血模型
- 实体类仅包含getter/setter方法
- 缺乏真正的业务行为和领域逻辑
2.2 贫血模型问题的深度解析
传统MVC架构中的实体对象仅仅是数据载体,缺乏业务行为,这一根本性缺陷导致:
2.2.1 业务规则验证分散
- 参数校验逻辑分布在Controller层
- 业务状态校验分布在Service层核心计算逻辑以私有方法形式隐藏在Service中
2.2.2 数据一致性难以保证
- 缺乏明确的聚合边界
- 事务管理复杂且容易出错
2.2.3 可测试性差
- 单元测试需要启动整个Spring容器
- 必须Mock大量依赖组件
- 测试用例编写和维护成本高昂
三、DDD战略设计:划定业务边界
我们必须先进行战略设计,以识别和定义清晰的业务边界。战略设计方法详见博客《领域驱动设计(DDD)脉络与项目案例分析》的第二章战略设计篇。
3.1 识别核心子域
基于项目经验,智慧园区系统可识别为:
- 园区资产管理子域(核心域)
- 招商租赁子域(核心域)
- 合同管理子域(支撑域)
- 财务结算子域(支撑域)
3.2 定义限界上下文
空间管理上下文
- 核心职责:园区空间资源状态管理
- 领域概念:空间、空间状态、空间类型
3.3 上下文映射关系
- 空间管理上下文 → 租赁交易上下文:通过"领域事件"异步协作
- 统一通用语言:如"订单已确认"事件取代"修改订单状态"操作
四、DDD战术实现:代码改造实践
4.1 改造前:MVC架构代码
实体类(贫血模型)
@Data
public class ParkSpace {
private Long id;
private String spaceCode;
private String spaceType;
private BigDecimal area;
private String status;
}
@Data
public class LeasingOrder {
private Long id;
private Long spaceId;
private String customerName;
private String customerPhone;
private LocalDate startDate;
private LocalDate endDate;
private BigDecimal totalRent;
private String orderStatus;
private Long contractDraftId;
}
Service层(上帝服务)
@Service
public class SpaceLeasingService {
@Autowired
private ParkSpaceMapper parkSpaceMapper;
@Autowired
private LeasingOrderMapper leasingOrderMapper;
@Autowired
private ContractService contractService;
public LeasingOrder applyForLeasing(LeasingApplyRequest request) {
// 参数验证
if (request.getCustomerName() == null) {
throw new IllegalArgumentException("客户姓名不能为空");
}
// 查询并校验空间状态
ParkSpace targetSpace = parkSpaceMapper.selectById(request.getSpaceId());
if (!"空闲".equals(targetSpace.getStatus())) {
throw new IllegalStateException("该空间当前不可租赁");
}
// 创建租赁订单
LeasingOrder newOrder = new LeasingOrder();
newOrder.setSpaceId(request.getSpaceId());
newOrder.setCustomerName(request.getCustomerName());
newOrder.setCustomerPhone(request.getCustomerPhone());
newOrder.setStartDate(request.getStartDate());
newOrder.setEndDate(request.getEndDate());
newOrder.setTotalRent(calculateTotalRent(targetSpace, request)));
newOrder.setOrderStatus("待签约");
leasingOrderMapper.insert(newOrder);
// 更新空间状态
targetSpace.setStatus("已预定");
parkSpaceMapper.updateById(targetSpace);
// 调用远程合同服务
ContractDraftDTO contractDraft = contractService.generateContractDraft(
new ContractDraftRequest(newOrder.getId(), request.getStartDate(), request.getEndDate())));
newOrder.setContractDraftId(contractDraft.getId());
return newOrder;
}
}
4.2 改造过程:DDD重构
4.2.1 聚合根设计
public class Space implements AggregateRoot {
private SpaceId id;
private SpaceCode code;
private SpaceType type;
private Area area;
private SpaceStatus status;
public LeasingOrder applyForLeasing(CustomerInfo customer, LeaseTerm leaseTerm, Rental rental){
if (!this.status.isAvailable()) {
throw new IllegalStateException("该空间当前不可租赁");
}
LeasingOrder newOrder = LeasingOrder.create(this.id, customer, leaseTerm, rental);
this.status = SpaceStatus.RESERVED;
this.registerDomainEvent(new SpaceReservedEvent(this.id, newOrder.getId()));
return newOrder;
}
}
4.2.2 值对象封装
public class CustomerInfo implements ValueObject {
private final String name;
private final String phone;
public CustomerInfo(String name, String phone) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("客户姓名不能为空");
}
this.name = name.trim();
this.phone = phone.trim();
}
}
4.3 改造后:DDD架构代码
4.3.1 充血的领域模型
public class LeasingOrder implements AggregateRoot {
private LeasingOrderId id;
private SpaceId spaceId;
private CustomerInfo customer;
private LeaseTerm leaseTerm;
private Rental rental;
private OrderStatus status;
public static LeasingOrder create(SpaceId spaceId, CustomerInfo customer, LeaseTerm leaseTerm, Rental rental){
if (customer == null) {
throw new IllegalArgumentException("客户信息不能为空");
}
LeasingOrder order = new LeasingOrder();
order.id = LeasingOrderId.nextId();
order.spaceId = spaceId;
order.customer = customer;
order.leaseTerm = leaseTerm;
order.rental = rental;
order.status = OrderStatus.PENDING_SIGN;
order.registerDomainEvent(new LeasingOrderCreatedEvent(order.id));
return order;
}
}
4.3.2 应用服务层
@Service
@Transactional
public class SpaceLeasingApplicationService {
@Autowired
private SpaceRepository spaceRepository;
@Autowired
private LeasingOrderRepository leasingOrderRepository;
public void applyForLeasing(ApplyForLeasingCommand command) {
Space space = spaceRepository.findById(command.getSpaceId()))
.orElseThrow(() -> new SpaceNotFoundException("空间不存在")));
LeasingOrder newOrder = space.applyForLeasing(command.getCustomer(), command.getLeaseTerm(), command.getRental()));
leasingOrderRepository.save(newOrder);
spaceRepository.save(space);
}
}
4.4 改造总结:DDD 四层架构落地全景
通过 MVC 到 DDD 的代码重构,最终形成 “接口层→应用层→领域层(核心)→基础设施层” 的四层架构。各层职责清晰、边界明确,既保障了业务逻辑的内聚,又实现了技术细节的隔离,具体架构分工如下:
| 架构分层 | 核心职责 | 本项目落地示例 | 设计原则 |
| 接口层 | 暴露外部访问入口,适配请求 / 响应格式,承接外部交互 | 控制器(如 SpaceLeasingController,接收前端请求)、DTO 转换、参数校验器 | 面向外部访问,仅做 “适配转换”,不处理任何业务逻辑 |
| 应用层 | 协调用例流程、组装领域能力、管理事务边界,不包含核心业务规则 | 应用服务(SpaceLeasingApplicationService)、命令对象(ApplyForLeasingCommand) | 轻量级协调,仅负责 “接收接口层请求→调用领域层→触发持久化”,不侵入核心业务逻辑 |
| 领域层(核心) | 封装核心业务逻辑、业务规则、领域模型,定义抽象依赖接口(如仓储接口) | 聚合根(Space、LeasingOrder)、值对象(CustomerInfo、LeaseTerm)、领域事件(SpaceReservedEvent)、仓储接口(SpaceRepositoryInterface) | 纯业务聚焦,不依赖任何技术框架 / 其他层,是整个架构的稳定核心 |
| 基础设施层 | 实现领域层抽象接口、提供技术实现细节,适配外部依赖 | 仓储实现(SpaceRepositoryImpl、LeasingOrderRepositoryImpl)、RPC 客户端(ContractService 适配器) | 依赖倒置,通过接口适配领域层需求,隔离数据库、外部服务等技术细节,为其他层提供底层支撑 |
架构核心优势总结
- 职责内聚:业务规则集中在领域层,技术实现隔离在基础设施层,修改互不影响。
- 可测试性:领域层可脱离 Spring 容器独立测试,无需 Mock 大量依赖。
- 扩展性:新增业务功能时,只需扩展领域模型或应用服务,基础设施层适配即可(如新增 “续租” 功能,仅需在 LeasingOrder 聚合根新增 renew () 方法)。
- 微服务适配:基于限界上下文划分的四层架构,可直接通过添加注解(如 @DubboService、@RestController)暴露为微服务,无需重构核心逻辑(见第六章微服务升级路径)。
五、架构演进效果验证
5.1 功能一致性对比
| 功能要点 | MVC实现 | DDD实现 | 一致性 |
| 客户信息验证 | Service方法中if语句 | Command构造函数验证 | 完全一致 |
| 空间状态验证 | if (!"空闲".equals(status)) | if (!this.status.isAvailable())) | 逻辑等价 |
| 订单创建 | new LeasingOrder() | LeasingOrder.create() | 结果相同 |
| 合同生成 | 直接RPC调用 | 领域事件驱动 | 最终状态一致 |
5.2 可测试性提升
MVC测试复杂度高
@SpringBootTest
class SpaceLeasingServiceTest {
@MockBean
private ParkSpaceMapper parkSpaceMapper;
@MockBean
private ContractService contractService;
}
DDD测试简单直接
class SpaceTest {
@Test
void testApplyForLeasing_Success() {
Space space = new Space(...);
space.setStatus(SpaceStatus.AVAILABLE);
LeasingOrder order = space.applyForLeasing(customer, leaseTerm, rental);
assertThat(space.getStatus()).isEqualTo(SpaceStatus.RESERVED);
}
}
六、微服务升级路径
6.1 平滑升级策略
基于清晰的限界上下文划分,微服务拆分变得异常简单。只需在原有的应用服务上添加服务暴露注解即可完成微服务化改造。
服务暴露实现:
// 在应用服务上简单添加注解,即可暴露为微服务接口
@Service
@DubboService // 或者 @RestController
@Transactional
public class SpaceLeasingApplicationService {
// 内部实现与DDD阶段完全一致,无需改动!
}
6.2 事件驱动架构实现
6.2.1 事件发布机制
通过领域事件实现服务间解耦,具体流程如下:
- 订单服务发布SpaceReservedEvent领域事件
- 合同服务监听该事件并自动生成合同草稿
- 库存服务监听事件进行库存预占
- 通知服务监听事件发送确认消息
6.2.2 事件处理流程
// 在合同服务中,监听“空间租赁订单创建”事件
@Component
public class ContractEventHandler {
@DubboReference
private ContractApplicationService contractAppService;
@EventListener
public void handleLeasingOrderCreated(LeasingOrderCreatedEvent event) {
// 接收到事件后,在合同服务的上下文内生成合同
contractAppService.generateContractForOrder(event.getOrderId());
}
}
6.3 技术架构升级
6.3.1 服务治理组件
- 服务注册与发现:Nacos / Eureka
- 配置中心:Apollo
- 服务网关:Spring Cloud Gateway
- 链路追踪:SkyWalking
6.3.2 数据一致性保障
- 基于Saga模式的分布式事务管理
- 最终一致性保证机制
- 补偿事务处理逻辑
6.4 部署架构优化
6.4.1 容器化部署
- Docker容器封装
- Kubernetes集群管理
- 持续集成/持续部署流水线
七、DDD的适用边界与工程化建议
7.1 DDD的适用场景
- 业务逻辑复杂且频繁变化:系统包含大量的业务规则、状态流转和校验逻辑,如易达慧云项目,涉及招商、租赁、合同、财务等多个复杂业务域。
- 需要长期演进:业务会不断发展变化,系统需要长期维护。
- 团队规模较大:需要清晰的模块边界来规范多个团队或开发者之间的协作。
7.2 DDD的陷阱与不适用场景
- 简单CRUD系统:对于仅涉及增删改查、几乎没有业务规则的系统,使用DDD会导致"类爆炸",开发效率极低,属于典型的过度设计。(如报表系统)
- 业务边界清晰且稳定的系统。
7.3 工程化最佳实践
- 渐进式重构:选择核心、复杂且频繁变动的子域作为试点,不要试图一次性将整个系统DDD化。
- 团队共识建设:确保整个团队对DDD的基本理念和项目的通用语言有共同的理解。
- 工具链支持:建立代码生成、测试框架等配套工具。
- 避免教条主义:不要为了追求"完美的DDD"而过度设计。DDD是工具,不是目的。
结语
有了限界上下文的划分,单体、微服务、事件驱动等架构仅是领域之间不同的协作方式,而领域本身需保持稳定。
通过这个功能要点的改造案例,我们可以清晰地看到DDD工程化实践的核心价值:DDD的真正力量不在于创造新的功能,而在于以一种更清晰、更健壮、更易于演进的方式来实现这些功能。
它通过限界上下文为我们提供了划分微服务边界的科学依据,通过聚合根保证了业务规则的一致性,通过领域事件实现了服务间的解耦。DDD提供的这套结构化思维方法和建模工具,将成为架构决策的可靠指南。
📚 我的技术博客导航:[点击进入一站式查看所有干货]
工程化实践:从MVC到DDD的代码重构&spm=1001.2101.3001.5002&articleId=154874531&d=1&t=3&u=d922308eb18248a5afcec08252813de3)
212

被折叠的 条评论
为什么被折叠?



