那个周末,我推掉了所有社交活动,花了两天时间读完了 Vaughn Vernon 的《实现领域驱动设计》(Implementing Domain-Driven Design)。这本书很厚,六百多页,也很难读,里面有大量的概念和代码示例。但读完之后,我对软件设计的理解发生了很大的变化。本文记录读书过程中的思考和感悟,聊聊DDD到底改变了我什么。
一、为什么会读这本书
我做后端开发有几年了,一直在用经典的三层架构:Controller、Service、DAO。这种架构用起来很顺手,大部分业务场景都能应付。但随着做的项目越来越复杂,我开始感觉到一些问题:
- Service层越来越臃肿,一个Service类有几千行代码
- 业务逻辑散落在各个地方,Controller里有、Service里有、甚至DAO里也有
- 领域概念不清晰,代码里全是DTO、VO、PO,真正的业务对象反而没有
- 需求变更的时候,改一个地方要动很多文件,经常改出Bug
- 和产品经理沟通的时候,用的语言完全不一样,他们说"订单",我脑子里想的是"order表"
我知道这些问题的存在,但不知道怎么解决。直到有一次和一个架构师聊天,他推荐我去读DDD(领域驱动设计)的书。我先读了Eric Evans的《领域驱动设计》,那本更偏向理论,读得一知半解。然后又读了这本《实现领域驱动设计》,这本更偏向实践,有大量的代码示例,读完之后才真正理解了DDD。
二、这本书在讲什么
《实现领域驱动设计》是DDD的实践指南,它不讲太多理论,而是告诉你怎么在实际项目中落地DDD。
书的核心内容包括:
- 领域、子域和限界上下文:怎么把复杂的业务领域拆分成多个子域,怎么划分限界上下文
- 聚合和聚合根:怎么设计聚合,怎么确定聚合的边界,怎么保证聚合内的一致性
- 实体和值对象:什么时候用实体,什么时候用值对象,两者的区别是什么
- 领域服务和应用服务:业务逻辑应该放在哪里,领域服务和应用服务的职责怎么划分
- 仓储和工厂:怎么设计仓储接口,怎么用工厂创建复杂对象
- 领域事件:怎么用领域事件解耦,怎么实现最终一致性
- CQRS和事件溯源:命令查询职责分离,用事件来存储状态变化
这些概念单独看都不难,但组合在一起,就形成了一套完整的软件设计方法论。
三、读书过程中的几个顿悟
读这本书的时候,有几个瞬间让我有"原来如此"的顿悟感。
顿悟一:代码应该反映业务,而不是反映数据库。
我以前写代码,是先设计数据库表,然后根据表结构写实体类,再写Service。这种方式下,代码的结构是由数据库决定的,而不是由业务决定的。
DDD的思路正好相反:先理解业务,提炼出领域概念,设计领域模型,然后再考虑怎么持久化。数据库只是存储领域模型的一种方式,不应该反过来主导领域模型的设计。
这个观点对我冲击很大。我回头看自己写的代码,发现很多"实体类"其实只是数据库表的映射,根本不是真正的领域对象。它们没有行为,只有getter和setter,是典型的"贫血模型"。
而DDD提倡的是"充血模型":领域对象不仅有数据,还有行为,业务逻辑封装在领域对象内部。比如一个Order对象,不仅有订单状态、金额这些数据,还有pay()、cancel()、ship()这些行为方法。这样业务逻辑就内聚了,不会散落在各处。
顿悟二:限界上下文是解决复杂系统的关键。
复杂的业务系统之所以难维护,很大程度上是因为领域概念混乱。同一个词,在不同的业务场景下有不同的含义。比如"用户",在注册场景下是一个含义,在订单场景下是另一个含义,在客服场景下又是一个含义。如果把这些都放在一个User类里,这个类就会变得无比复杂。
DDD的解决方案是限界上下文(Bounded Context):每个上下文有自己的领域模型,同一个词在不同上下文里可以是不同的对象。上下文之间通过明确的接口(防腐层、开放主机服务等)来交互。
这个思路让我豁然开朗。以前我总想设计一个"万能"的User类,结果越设计越复杂。现在我明白了,不需要万能类,每个上下文有自己的User就好,上下文之间做好映射就行。
顿悟三:聚合的边界是一致性边界。
聚合(Aggregate)是DDD里最重要也最难理解的概念之一。以前我以为聚合就是把相关的对象放在一起,比如订单聚合包含订单和订单项。
读完书我才明白,聚合的本质是一致性边界。一个聚合内的所有对象,必须在任何时候都保持业务一致性。修改聚合的时候,必须作为一个整体来修改,不能只修改聚合内的一部分而不考虑其他部分。
这就决定了聚合不能太大。如果聚合包含太多对象,每次修改都要加载整个聚合,性能会很差,并发冲突也会很多。所以聚合的设计原则是:在保证一致性的前提下,尽量小。
这个原则让我重新审视了很多设计。以前我喜欢把所有相关的对象都塞进一个聚合里,觉得这样"内聚"。现在我知道,那不是内聚,是耦合。真正的内聚是基于业务一致性的,而不是基于数据关系的。
顿悟四:领域事件是解耦的利器。
以前系统之间的交互,我都是用同步调用。比如订单支付成功后,要调用库存服务扣库存、调用积分服务加积分、调用通知服务发消息。这样做的问题是:订单服务和其他服务强耦合,任何一个服务挂了,订单支付就失败;而且调用链很长,性能差。
DDD里的领域事件(Domain Event)提供了另一种思路:订单支付成功后,发布一个OrderPaid事件,库存服务、积分服务、通知服务各自订阅这个事件,异步处理。这样订单服务只需要发布事件,不需要关心谁来处理,耦合度大大降低。
而且领域事件还有一个好处:它是业务事实的记录。"订单已支付"这个事件,比"调用了扣库存接口"更能反映业务本质。用事件来驱动系统,系统的设计会更贴近业务。
四、这本书改变了我什么
读完这本书,我的设计思维发生了几个根本性的变化。
1. 从"数据驱动"到"领域驱动"。
以前我设计系统,先想的是"有哪些表",现在先想的是"有哪些业务概念,它们之间是什么关系"。数据库设计退居其次,领域模型成为设计的核心。
2. 从"贫血模型"到"充血模型"。
以前我的实体类只有getter和setter,业务逻辑全在Service里。现在我会把业务逻辑封装在领域对象里,Service变得很薄,只负责协调领域对象和外部服务。
3. 从"大一统"到"分而治之"。
以前我总想设计一个统一的、通用的模型,结果越设计越复杂。现在我会主动划分限界上下文,每个上下文有自己的模型,上下文之间用明确的接口交互。
4. 从"同步调用"到"事件驱动"。
以前系统之间的交互优先考虑同步调用,现在我会先想能不能用事件驱动。能异步的就异步,能解耦的就解耦。
5. 从"技术语言"到"业务语言"。
以前和产品经理沟通,我经常说"这个表加个字段""这个接口改一下"。现在我会尽量用业务语言沟通,比如"订单在支付状态下才能发货""会员等级达到银卡才能享受折扣"。统一语言(Ubiquitous Language)是DDD的基础,也是高效沟通的前提。
五、这本书的局限和我的保留意见
当然,DDD不是银弹,这本书也不是完美的。
1. DDD不适合所有项目。 DDD适合复杂的业务系统,尤其是领域逻辑复杂、需求频繁变化的系统。如果是简单的CRUD系统,用DDD反而会过度设计,增加复杂度。
2. 学习曲线陡峭。 DDD的概念很多,而且很多概念比较抽象,不容易理解。真正掌握DDD,需要大量的实践和反思,不是读一两本书就能学会的。
3. 落地难度大。 理论上DDD很好,但实际落地的时候会遇到很多阻力:团队不理解、框架不支持、时间不允许、老系统没法改。很多团队学了DDD之后,最后只学到了"分层"和"DTO",核心的领域建模反而没做到。
4. 有些观点偏理想化。 比如书中强调的"聚合内强一致性",在高并发场景下很难做到,很多时候只能用最终一致性。还有"每个限界上下文有自己的数据库",在实际项目中也很难完全做到。
所以我的态度是:学习DDD的思想,根据项目的实际情况选择性地应用,而不是全盘照搬。
六、给想读这本书的人的建议
- 先有一定的项目经验再读。 如果你刚入行,还没有做过复杂项目,读这本书可能会觉得不知所云。最好有两三年开发经验,遇到过架构问题之后再读,会更有共鸣。
- 先读《领域驱动设计》再读这本。 Eric Evans的《领域驱动设计》是理论基础,Vaughn Vernon的这本是实践指南。先读理论,再读实践,理解会更深刻。
- 边读边想自己的项目。 读的时候,把书中的概念和自己做过的项目对照起来想:如果用DDD重做这个项目,会怎么设计?这样读才不会空对空。
- 不要期望一次读懂。 这本书内容很多,第一次读可能只能理解30%。没关系,先建立整体印象,然后在实践中慢慢体会,过一段时间再读,会有新的理解。
- 读完之后要实践。 读书只是开始,真正的理解来自实践。可以在一个小项目中尝试用DDD的方式设计,哪怕只用了其中一两个概念,也会有收获。
七、写在最后
那个周末读完《实现领域驱动设计》,我合上书,坐在窗前想了很久。我回顾了自己做过的项目,发现很多问题的根源,都是因为没有从业务角度出发去设计,而是被技术和数据库牵着走。
DDD给我的最大启发,不是某个具体的技术或模式,而是一种思维方式:软件的核心是业务,技术只是实现业务的手段。好的软件设计,应该让代码反映业务,让技术服务于业务,而不是反过来。
这种思维方式的改变,比学会任何一个框架或工具都更有价值。因为框架会过时,工具会换代,但对业务本质的理解和建模能力,是程序员的核心竞争力,不会随着技术迭代而贬值。
如果你也是一个后端开发者,做过一些项目,开始感觉到架构和设计的重要性,我强烈推荐你读一读这本书。它可能不会立刻让你的代码变好,但它会改变你思考软件设计的方式,而这种改变,会在未来的项目中慢慢显现出来。
最后用一句话结束本文:好的代码不是写出来的,是设计出来的;好的设计不是靠技术,是靠对业务的理解。DDD教给我的,就是如何理解业务,如何把业务转化为代码。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录