前几年,我全程采用DDD开发了一个一物一码管理平台——码如云,并成功上线。在这个过程中,我们学了很多,积累了很多,也踩了不少的坑。在本文中,我希望通过FAQ(Frequently Asked Questions,常见问题)的方式给大家分享一下我对DDD的一些看法。如果对DDD落地感兴趣,可以参考我的DDD落地文章系列。
问:DDD最大的受益者是谁?
答:架构师和程序员,请注意,这里的架构师是那些工作在前线,至少会参与软件模型设计的架构师,最好是依然参与代码编写工作。
问:搞DDD需要哪些条件?
答:建议先写3年左右的代码,在熟悉了面向对象技术后再学习DDD可能更加容易上手。
问:DDD为什么有时也被称为”玄学”?
答:很多对DDD的解读比较空洞,纸上谈兵,无法指导软件项目的实际落地,这种对DDD虚无缥缈的解读被有些人称为”玄学”。
问:哪些人可以做领域专家?
答:任何熟悉业务的人,比如银行的前台工作人员对于银行业务系统来说则可认为是领域专家。
问:业务分析师(BA)需要了解DDD吗?
答:不需要,BA需要做的事情是理清业务,然后将业务传递给架构师或程序员。
问:DDD的战略设计是什么?
答:DDD的战略设计只在解决一个问题,即软件的模块化划分问题。
问:DDD与微服务是什么关系?
答:没关系,业界有个说法是“DDD的限界上下文可以帮助指导微服务的划分”,但是这个说法过于笼统和牵强,基本不具现实指导意义。
问:DDD当下为什么这么火?
答:可能是跟风者比较多吧,或者是前面提到的与微服务那种牵强的关系,DDD就是一个工具而已,原本不应该这么火的。
问:DDD与TOGAF有什么关系?
答:没有关系,TOGAF主要用于企业整体架构,而DDD侧重于一个具体业务系统的软件设计和落地。
问:搞DDD必须搞事件风暴吗?
答:不用,事件风暴主要用于帮助我们了解业务流程,在实践中事件风暴很容易陷入“为了搞事件风暴而搞事件风暴”的陷阱,建议采用更加朴素的方式梳理业务。
问:DDD与面向对象是什么关系?
答:可以认为DDD是面向对象进阶,这也是为什么前面有建议说熟悉了OO之后再搞DDD。
问:DDD的战术设计包含哪些概念?
答:包含应用服务,聚合根,领域服务,实体,值对象,工厂,领域事件,资源库等。
问:为什么建议将业务概念优先建模为值对象而不是实体?
答:因为值对象是不可变的,可以大大降低系统的信息熵进而降低程序员的负担,并且可以方便逻辑推理和系统调试。
问:怎么理解聚合根?
答:聚合根表示业务中那些顶级的实体对象,其内部数据相互紧密联系,即“聚合”在一起。比如,电商系统中的“订单(Order)”,CRM系统中的“客户(Customer)”均是典型的聚合根对象。
问:不变条件是什么意思?
答:不变条件表示在聚合根中,那些具有业务互动性的业务逻辑,不变条件必须在同一个聚合根的公有方法中得到满足,否则容易导致业务逻辑的泄漏。一个老生常谈的例子是订单(Order),修改订单项内容后,订单价格也应该随之变化,因此对订单项的修改和对价格的修改应该放到同一个方法(比如updateOrderItems()
)中。
问:事务边界应该放在哪里?
答:应用服务,因为应用服务中的共有方法和业务用例一一对应,而业务用例又与事务一一对应。
问:什么时候应该用领域服务?
答:当将业务逻辑放在聚合根中不合适的时候,才考虑创建领域服务来存放这些业务逻辑。比如,在更新成员手机号时,需要检查手机号是否已经被他人占用,这种跨聚合根的业务逻辑无法放到某一个成员对象中,此时应该采用领域服务。
问:应用服务和领域服务的区别是什么?
答:应用服务和领域服务是很不一样的概念,应用服务是领域模型的门面,所有外部请求都由应用服务的调度编排后进入领域模型中,而领域服务是属于领域模型的一部分。应用服务不包含业务逻辑,领域服务则相反。
问:如何保证发送领域事件和更新聚合根之间一致性?
答:采用事件发送表,即先将事件保存到与聚合根相同的数据库中,这样通过数据库的本地事务即可完成它们之间的数据一致性,然后再通过一个单独的组件从事件表中加载领域事件再发送出去。更多详情,请参考这里。
问:DDD和CQRS是什么关系?
答:没关系,不过在DDD项目中通常会采用CQRS,以得到更加纯粹的领域模型,当然CQRS的作用并不止于此。
问:有推荐的DDD书籍吗?
答:《领域驱动设计:软件核心复杂性应对之道》(蓝皮书),《实现领域驱动设计》(红皮书),《领域驱动设计模式、原理与实践》,《解构领域驱动设计》等。