目录
一、前言
宏观上:
- 互联网业务发展初期“小步快跑,迭代试错”的情形下要求系统需要快速迭代,但是随着互联网公司逐渐深入实体经济,业务日益复杂,我们在开发中也越来越多地遇到传统行业软件开发中所面临的问题。
- 传统的研发模式中,系统分析和设计是分开的,导致需求和成品非常容易出现偏差,两者相对独立,还会导致沟通困难。
- DDD打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。
微观上:
- Action/Service/DAO这种分层架构下,开发模式通常是面向过程、面向数据的。这种开发模式下,对象只是数据的载体,没有行为。整个开发的过程都是以数据为中心,以数据库ER设计为驱动,面向数据进行开发,即数据驱动设计。
- 业务逻辑都是写在Service中,简单的业务系统采用这种贫血模型和过程化设计是没有问题的,但当业务逻辑复杂了,业务逻辑、状态会散落到在大量方法中,原本的代码意图会渐渐不明确,我们将这种情况称为由贫血症引起的失忆症。
- 随着业务的不断发展,业务逻辑变得越来越复杂,系统的实现逻辑也变得越来约复杂,最后的结果就是没有人能够描述清楚每个细节,当对现有功能进行迭代时,光回顾该功能涉及的流程(涉及的改动点)就需要很长的时间,而且还容易出现梳理遗漏的情况。
- 这种情形下,rd往往通过[新写一套逻辑+开关控制]的方式来完成这次的迭代,该功能之后再经历几次迭代后,同一功能在系统中可能存在多个版本的代码,这样就形成恶性循环,每次迭代都需要花费大量的时间去回顾之前多个版本的代码。
- 最终,当迭代的成本超过了重构的成本,rd就开始对该功能进行重构,重构是克服演进式设计中大杂烩问题的主力,通过在单独的类及方法级别上做一系列小步重构来完成,我们可以很容易重构出一个独立的类来放某些通用的逻辑,但是,重构时会发现你很难给它一个业务上的含义,只能给予一个技术维度描绘的含义。这会带来什么问题呢?新同学并不总是知道对通用逻辑的改动或获取来自该类。显然,制定项目规范并不是好的idea。我们又闻到了代码即将腐败的味道。
- 更好的做法是采用领域模型的开发方式,将数据和行为封装在一起,并与现实世界中的业务对象相映射。各类具备明确的职责划分,将领域逻辑分散到领域对象中。
其它分享:
- 数据驱动设计 VS 领域驱动设计:领域驱动设计峰会-领域驱动设计大揭秘
二、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-领域驱动设计分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/11633.html