说下你对DDD的理解
来源:说下你对DDD的理解
一、 标准面试回答模版(建议背诵)
面试官: 谈谈你对 DDD 的理解,你在项目中是怎么落地的?
Fox版标准回答: “如果你上来就跟我谈‘四层架构’、‘Repository’,那你只是学了 DDD 的皮毛。 DDD 不是一套框架,而是一种处理高度复杂业务的思维方式。
我对 DDD 的理解,分为‘战略设计’和‘战术设计’两个层面:
- 战略设计(Strategic Design)—— 这是架构师的战场:
- 核心是为了划定边界。通过 通用语言(Ubiquitous Language) 和业务专家对齐认知,识别出 核心域、支撑域和通用域。
- 最重要的是划分 限界上下文(Bounded Context)。在微服务架构中,一个限界上下文通常就对应一个微服务。这解决了‘微服务怎么拆’的世界级难题。
- 战术设计(Tactical Design)—— 这是程序员的战场:
- 核心是为了保证业务逻辑的高内聚。
- 我们要定义 聚合(Aggregate) 和 聚合根(Aggregate Root) 来保证数据的一致性边界。
- 我们要区分 实体(Entity) 和 值对象(Value Object)。
- 最关键的是,我们要摒弃 贫血模型(Anemic Model),转向 充血模型(Rich Model),把业务逻辑封装在领域对象内部,而不是散落在 Service 层。
一句话总结: DDD 的本质,是让软件架构与业务领域保持高度一致,从而控制业务复杂度。”
二、 核心原理与代码层面的体现
1. 场景一:贫血模型 vs 充血模型(最能体现 DDD 味道的地方)
面试官潜台词: 你写的代码是面向对象(OO)还是面向过程?
贫血模型(传统 Spring 开发模式):
User 对象只有
@Data(Getter/Setter)。业务逻辑全在
UserService里。Fox 点评: “这不叫面向对象,这叫‘披着对象外衣的面向过程’。Service 层臃肿不堪,Entity 沦为单纯的数据载体。”
充血模型(DDD 模式):
User 对象拥有业务行为。
代码演示:
// 贫血模型写法 (反例)
userService.changePassword(userId, oldPwd, newPwd);
// 逻辑全在 Service 里,User 只是个数据包
// DDD 充血模型写法 (正例)
public class User {
// 状态和行为在一起
public void changePassword(String oldPwd, String newPwd) {
if (!this.password.equals(encrypt(oldPwd))) {
throw new BizException("旧密码错误");
}
this.password = encrypt(newPwd);
// 发布领域事件
addDomainEvent(new PasswordChangedEvent(this.id));
}
}
// Service 层变得极薄
public void changePasswordAppService(String userId, ...) {
User user = userRepository.find(userId);
user.changePassword(old, new); // 调用领域对象行为
userRepository.save(user);
}2. 场景二:值对象(Value Object)的妙用
面试官潜台词: 你知道什么是不可变对象吗?
- 地址(Address) 就是个典型的值对象。
- 它没有 ID,只有属性(省、市、区)。
- 两个地址如果属性完全一样,它们就是相等的。
- 代码体现: 在 DDD 中,修改地址不是
user.setProvince("xx"),而是直接替换整个对象:user.changeAddress(new Address("北京", "海淀", ...))。
三、 Fox 的深度解析
如果面试官追问:“DDD 这么好,为什么国内很多公司推行不下去?” 或者 “你是怎么划分聚合的?”
Fox版解析:
1. 关于 DDD 落地难的真相: “最大的阻力不是技术,而是‘认知成本’和‘团队能力’。
- DDD 要求开发人员必须成为业务专家。但现实是,很多开发只关注‘表怎么建’,根本不关心业务全貌。
- 如果团队还在用‘数据库驱动开发’(先建表,再写代码),那推行 DDD 必死无疑。DDD 是领域优先,最后才考虑数据库怎么存(Repository 也就是个接口而已)。”
2. 关于聚合设计的铁律: “聚合设计的核心不是‘大’,而是‘一致性边界’。
- 原则: 在一个事务中,你只应该修改一个聚合。
- 反例: 很多人把‘订单’和‘用户’设计在同一个聚合里,试图在一个事务里既改订单状态又扣用户余额。错!
- 正解: 订单是订单聚合,用户是用户聚合。它们之间应该通过领域事件(Domain Event)进行最终一致性的解耦。这才是分布式系统的玩法。”
3. 总结(满分收尾): “DDD 是一把屠龙刀,用来对付‘复杂业务’这条恶龙。 如果你的系统只是简单的 CRUD(比如后台管理系统),千万别用 DDD! 那是杀鸡用牛刀,纯属过度设计。 架构师的价值,在于知道什么时候该用 DDD,什么时候该用 CRUD。”