置顶 A__17 已于 2024-04-08 17:22:44 修改

目录

一、前言

二、DDD是什么?

三、DDD的职责:

复杂系统的应对:

微服务架构:

DDD架构:

四、DDD相关概念:

领域

限界上下文(Bounded Context)

统一语言

五、DDD的实现:

战略建模

内容:

划分限界上下文

限界上下文之间的映射关系(Context Mapping)

战术建模:

内容:

模块(Module):

实体(Entity):

值对象(Value Objects)

聚合(Aggregate)

领域服务(Domain Services)

领域事件(Domain Events)

代码分层架构:

DDD落地应对的挑战:


一、前言

宏观上:

  • 互联网业务发展初期“小步快跑,迭代试错”的情形下要求系统需要快速迭代,但是随着互联网公司逐渐深入实体经济,业务日益复杂,我们在开发中也越来越多地遇到传统行业软件开发中所面临的问题。
  • 传统的研发模式中,系统分析和设计是分开的,导致需求和成品非常容易出现偏差,两者相对独立,还会导致沟通困难。
  • DDD打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。

微观上:

  • Action/Service/DAO这种分层架构下,开发模式通常是面向过程、面向数据的。这种开发模式下,对象只是数据的载体,没有行为。整个开发的过程都是以数据为中心,以数据库ER设计为驱动,面向数据进行开发,即数据驱动设计
  • 业务逻辑都是写在Service中,简单的业务系统采用这种贫血模型和过程化设计是没有问题的,但当业务逻辑复杂了,业务逻辑、状态会散落到在大量方法中,原本的代码意图会渐渐不明确,我们将这种情况称为由贫血症引起的失忆症。
  • 随着业务的不断发展,业务逻辑变得越来越复杂,系统的实现逻辑也变得越来约复杂,最后的结果就是没有人能够描述清楚每个细节,当对现有功能进行迭代时,光回顾该功能涉及的流程(涉及的改动点)就需要很长的时间,而且还容易出现梳理遗漏的情况。
  • 这种情形下,rd往往通过[新写一套逻辑+开关控制]的方式来完成这次的迭代,该功能之后再经历几次迭代后,同一功能在系统中可能存在多个版本的代码,这样就形成恶性循环,每次迭代都需要花费大量的时间去回顾之前多个版本的代码。
  • 最终,当迭代的成本超过了重构的成本,rd就开始对该功能进行重构,重构是克服演进式设计中大杂烩问题的主力,通过在单独的类及方法级别上做一系列小步重构来完成,我们可以很容易重构出一个独立的类来放某些通用的逻辑,但是,**重构时会发现你很难给它一个业务上的含义,只能给予一个技术维度描绘的含义。这会带来什么问题呢?新同学并不总是知道对通用逻辑的改动或获取来自该类。显然,制定项目规范并不是好的idea。**我们又闻到了代码即将腐败的味道。
  • 更好的做法是采用领域模型的开发方式,将数据和行为封装在一起,并与现实世界中的业务对象相映射。各类具备明确的职责划分,将领域逻辑分散到领域对象中。

其它分享:

二、DDD是什么?

  • DDD(Domain-Driven Design):领域驱动设计是一套应对复杂软件系统分析和设计的建模方法论。

三、DDD的职责:

复杂系统的应对:

  • 系统的复杂度越来越来高是必然趋势,原因可能来自自身业务的演进,也有可能是技术的创新,然而一个人和团队对复杂性的认知是有极限的,就像一个服务器的性能极限一样,解决的办法只有分而治之,将大问题拆解为小问题,最终突破这种极限,架构设计的4个层面:
    • 业务架构——根据业务需求设计业务模块及其关系 ,关注系统的功能。
    • 系统架构——设计系统和子系统的模块,关注系统的整体性能。
    • 技术架构——决定采用的技术及框架,关注系统的实现方案(框架/缓存/..)
    • 工程架构——代码的分层设计

微服务架构:

  • 应对系统架构、技术架构上的挑战(主要是可用性/性能),通过注册中心、负载均衡、限流、熔断等方案来应对。

DDD架构:

  • 应对业务架构、工程架构上的挑战:通过战略建模和战术建模的方式将业务架构映射到系统架构上,在响应业务变化调整业务架构时,也随之变化系统架构。

四、DDD相关概念:

领域

概念:

  • 业务范围以及在其中所进行的活动称为领域。

分类:

  • 领域可以划分为子域,在领域划分过程中,会不断划分子域,子域按重要程度会被划分成三类:
    • 核心域:核心竞争力,业务成功的主要促成因素。
    • 通用域:不是核心,被整个业务系统使用。
    • 支撑域:不是核心,可以通过购买满足需求。

注意:

  • 在建设一个领域模型时,我们通常只关注核心业务(核心域),而不是试图创建一个全功能的领域模型,因为那样是十分困难的并且还容易导致建模失败。

限界上下文(Bounded Context)

概念:

  • 业务流程的实现方案/业务问题的解决方案(eg:软件系统等)称为限界上下文。

作用:

  • 限界上下文明确了领域模型的边界。

统一语言

  • 定义上下文的含义

  • 价值是可以解决不同角色(RD、PM、QA)间的交流障碍

五、DDD的实现:

战略建模

内容:

  • 于高层次、宏观上去划分和集成限界上下文。

划分限界上下文

  • 每个限界上下文专注于解决某个特定的子域的问题,每个子域都对应一个明确的问题,提供独立的价值,每个子域都相对独立,故各个限界上下文之间也是相对独立的。

限界上下文之间的映射关系(Context Mapping)

  • 合作关系(Partnership):两个上下文紧密合作的关系,一荣俱荣,一损俱损。
  • 共享内核(Shared Kernel):两个上下文依赖部分共享的模型。
  • 客户方-供应方开发(Customer-Supplier Development):上下文之间有组织的上下游依赖。
  • 遵奉者(Conformist):下游上下文只能盲目依赖上游上下文。
  • 防腐层(Anticorruption Layer):一个上下文通过一些适配和转换与另一个上下文交互。
  • 开放主机服务(Open Host Service):定义一种协议来让其他上下文来对本上下文进行访问。
  • 发布语言(Published Language):通常与OHS一起使用,用于定义开放主机的协议。
  • 大泥球(Big Ball of Mud):混杂在一起的上下文关系,边界不清晰。
  • 另谋他路(SeparateWay):两个完全没有任何联系的上下文。

战术建模:

这篇文章不错:DDD 战术设计

内容:

通过模块、聚合、实体、值对象、领域服务、领域事件等对象来细化限界上下文。

模块(Module):

  • 是一种控制限界上下文的手段,在工程中我们一般使用一个模块来表示一个领域的限界上下文。
  • 一般的工程中包的组织方式为{com.公司名.组织架构.业务.上下文.*},这样的组织结构能够明确的将一个上下文限定在包的内部。

实体(Entity):

  • 当一个对象由其标识(而不是属性)区分时,这种对象称为实体。

值对象(Value Objects)

  • 当一个对象用于对事务进行描述而没有唯一标识时,它被称作值对象。
  • 在实践中,需要保证值对象创建后就不能被修改,即不允许外部再修改其属性

聚合(Aggregate)

  • 聚合是一组相关对象(实体、值对象)的集合,作为一个整体被外界访问,聚合是为了保证领域内对象之间的一致性问题。
  • 如何创建好的聚合?
    • 聚合边界内必须明确有哪些信息,如果没有这些信息就不能称为一个有效的聚合。
    • 设计小聚合:
      • 大部分的聚合都可以只包含根实体(Aggregate Root),而无需包含其他实体。
      • 即使一定要包含,可以考虑将其创建为值对象。
    • 通过唯一标识来引用其他聚合或实体:
      • 当存在对象之间的关联时,建议引用其唯一标识而非引用其整体对象。
      • 如果是外部上下文中的实体,引用其唯一标识或将需要的属性构造值对象。
      • 如果聚合创建复杂,推荐使用工厂方法来屏蔽内部复杂的创建逻辑。
    • 边界内的内容具有一致性:
      • 在一个事务中只修改一个聚合实例。
      • 如果你发现边界内很难接受强一致,不管是出于性能或产品需求的考虑,应该考虑剥离出独立的聚合,采用最终一致的方式。
  • 常见场景:一个主记录对应多条明细记录的场景。
  • 设计方案:
    • 若可以有效控制明细数量,且明细数量较小时,主记录和明细记录可以设计为一个聚合。
    • 若明细数量可能很大时,考虑到性能的问题,我们应该将主记录和明细记录设计为两个聚合,当明细记录更新后发布领域事件,主记录根据明细更新事件做出相应的修改,这样就可以保证主记录和明细记录之间互不依赖。

领域服务(Domain Services)

  • 业务逻辑优先在聚合根边界内完成;聚合根中不合适放置的业务逻辑才考虑放到 DomainService 中。

领域事件(Domain Events)

  • 领域事件是对领域内发生的活动进行的建模。

代码分层架构:

ddd四层架构

DDD落地应对的挑战:

落地DDD需要应对的挑战

参考:

DDD 模式从天书到实践 - 知乎

领域驱动设计在互联网业务开发中的实践 - 美团技术团队

DDD战术设计,到底如何编码?