观其大略——领域驱动设计总结系列

观其大略——领域驱动设计概览

领域驱动设计诞生至今已有十数年岁月,这门似已步入老年的方法论因为微服务的兴起而重焕青春。它国外IT圈行之有效享有盛誉,国内却知音甚少。它从未大行其道,但一直在坚硬生长。

​ DDD起源于2004年 Eric Evans (埃里克·埃文斯) 发表了他最具影响的的作品——《Domain-Driven Design - Tackling Complexity in the Heart of Software》(领域驱动设计:软件核心复杂性应对之道)

领域驱动设计是针对复杂系统设计的一套软件工程方法,本质上,它是一个开放的设计方法体系

通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性 ;52核心思想就是将问题域逐步分解, 降低业务理解和系统实现的复杂度。

设计没有唯一的真相。

​ 从需求分析到落地的工具。DDD将重心放在业务上,从业务出发来设计代码,业务是DDD的中心。微服务架构大行其道的当今,我们面临的一个棘手问题是:如何识别和设计微服务?领域驱动的战略设计恰好可以在一定程度上解决此问题。

​ DDD 完全改变了传统软件开发工程师针对数据库进行的建模方法,从而将要解决的业务概念和业务规则转换为软件系统中的类型以及类型的属性与行为,通过合理运用面向对象的封装、继承和多态等设计要素,降低或隐藏整个系统的业务复杂性,并使得系统具有更好的扩展性,应对纷繁多变的现实业务问题。

领域驱动设计可能带来的价值:

  • DDD是一套完整而系统的设计方法,它能带给你从战略设计到战术设计的规范过程,让设计思路更清晰,过程更规范。

  • DDD 尤其善于处理于领域相关的高复杂度业务的产品研发,通过它可以为你的产品建立一个核心而稳定的领域模型内核,有利于知识的传递于继承。

  • DDD强调对架构和模型的精心打磨,尤其善于处理系统架构的演进设计。

    演进式架构,推荐王磊《演进式架构》的分享视频(第一个)

  • 领域驱动设计的思想、原则与模式有助于提高团队成员的面向对象设计能力与架构设计能力。

  • 领域驱动设计与微服务架构天生匹配,无论是在新项目中设计微服务架构,还是将系统从单体架构演进到微服务设计,都可以遵循领域驱动设计的架构原则。

现有方法的问题:

​ 面向数据的建模方法是关系数据库理论的延续,关注的是数据表以及数据表之间关系的设计。这是典型的面向技术实现的建模方法,面对日渐复杂的业务逻辑,这种设计方法欠缺灵活性与可扩展性,也无法更好地利用面向对象设计思想及设计模式,建立可重用的、可扩展的代码单元。领域驱动设计的提出,是设计观念的转变,蕴含了全新的设计思想、设计原则与设计过程

领域驱动设计过程

​ DDD 不是架构方法,也不是设计模式,它贯穿了整个软件的开发的生命周期,包括对需求的分析、建模、架构、设计、乃至最终的编码实现,甚至编码的测试和重构。

​ DDD强调领域模型的一致性,并通过模型驱动设计来保障领域模型于程序设计一致。从业务需求中提炼出通用语言,再基于通用语言建立领域模型; 领域模型会指导程序设计以及编码实践,最后再通过重构来发现隐式概念,并运用设计模式改进设计和开发质量。

​ 这个过程是一个覆盖软件全生命周期的设计闭环,每个环节的输出都可以作为下一个环节的输入,而在其中扮演重要指导作用的则是“领域模型”。这个设计闭环是一个螺旋式的迭代设计过程,领域模型会在这个迭代过程中逐渐演进,在保证模型完整性与正确性的同时,具有新鲜的活力,使得领域模型能够始终如一的贯穿领域驱动设计过程、阐释着领域逻辑、指导着程序设计、验证着编码质量。

​ 如果仔细审视这个设计闭环,会发现在针对问题域和业务期望提炼统一语言,并通过统一语言进行领域建模时,可能会面临高复杂度的挑战。这是因为对于一个复杂的软件系统而言,我们要处理的问题域实在太庞大了。在为问题域寻求解决方案时,需要从宏观层次划分不同业务关注点的子领域,然后再深入到子领域中从微观层次对领域进行建模。宏观层次是战略的层面,微观层次是战术的层面,只有将战略设计与战术设计结合起来,才是完整的领域驱动设计。

战略设计

战略设计的初衷是保持模型的完整性,从两个方面来考量,问题域架构

  • 问题域: 引入限界上下文(Bounded Context) 和上下文映射(Context Map) 对问题域进行合理的分解,识别出核心子域,和子领域,并确定领域的边界和他们之间的关系,维持模型的完整性。
  • 架构
    • 通过分层架构来分离关注点,尤其是将领域实现独立,更利于领域模型的单一性和稳定性。
    • 六边形架构,清晰表达领域和技术基础设施的边界
    • CQRS模式,分离了查询场景和命令场景,针对不同场景选择使用同步或者异步操作,来提高架构的低延迟性和高并发能力。

限界上下文保护了上下文的内部和其他上下文之间的领域概念互不冲突。同时分离关注点后,上下文成为了独立部署的设计单元。

战术设计

整个软件系统被分解为多个限界上下文(或领域)后,就可以分而治之,对每个限界上下文进行战术设计。领域驱动设计并不牵涉到技术层面的实现细节,在战术层面,它主要应对的是领域的复杂性。领域驱动设计用以表示模型的主要要素包括:

  • 值对象(Value Object)
  • 实体(Entity)
  • 领域服务(Domain Service)
  • 领域事件(Domain Event)
  • 资源库(Repository)
  • 工厂(Factory)
  • 聚合(Aggregate)
  • 应用服务(Application Service)

它们之间的关系如下:

领域驱动设计围绕着领域模型进行设计,通过分层架构(Layered Architecture)将领域独立出来。表示领域模型的对象包括:实体值对象领域服务领域逻辑都应该封装在这些对象中。这一严格的设计原则可以避免业务逻辑渗透到领域层之外,导致技术实现与业务逻辑的混淆。在领域驱动设计的演进中,又引入了领域事件来丰富领域模型。

聚合是一种边界,它可以封装一到多个实体值对象,并维持该边界范围之内的业务完整性。在聚合中,至少包含一个实体,且只有实体才能作为聚合根(Aggregate Root)。注意,在领域驱动设计中,没有任何一个类是单独的聚合,因为聚合代表的是边界概念,而非领域概念。在极端情况下,一个聚合可能有且只有一个实体。

工厂资源库都是对领域对象生命周期的管理。前者负责领域对象的创建,往往用于封装复杂或者可能变化的创建逻辑;后者则负责从存放资源的位置(数据库、内存或者其他 Web 资源)获取、添加、删除或者修改领域对象。领域模型中的资源库不应该暴露访问领域对象的技术实现细节。

演进的领域驱动设计过程

战略设计会控制和分解战术设计的边界与粒度,战术设计则以实证角度验证领域模型的有效性、完整性与一致性,进而以演进的方式对之前的战略设计阶段进行迭代,从而形成一种螺旋式上升的迭代设计过程,如下图所示:

​ 战略设计和战术设计两个不同阶段的设计目标是保持一致的,它们是一个连贯的过程,彼此之间又相互指导与规范,并最终保证一个有效的领域模型和一个富有表达力的实现同时演进。战略部分和战术部分,两者相辅相成。战略部分用于理解、梳理业务,找到核心业务,更好地划分系统。战术部分用于落地到代码上,用代码来清晰地表示业务,代码如何分层、如何设计都有一套成熟的指导方案

学习资料:

https://insights.thoughtworks.cn/tag/domain-driven-design/

https://zhuanlan.zhihu.com/p/146756977

https://static001.infoq.cn/resource/ebook/bf/bd/bf54748a33643e667156110202082cbd.pdf