领域驱动设计-对象建模

1. 过程式设计 Step

2. 对象建模 + 多态

逻辑多态:Step步骤建模(StateMachine)
模型多态:状态对象建模(StateEntity)

3. 领域驱动设计

1。 逻辑建立

自上而下的结构化分解:理清模型之间的关系

2. 模型建立

自下而上的面向对象分析:提升代码复用度和业务语义表达能力

  1. Domain对象:(领域持久化建模)
  • 唯一业务标识
  • 持有自己的业务属性和业务行为
  • 属性可变,有着自己的生命周期
  1. Value对象:(状态变更变量建模)
  • 可以有唯一业务标识 【区别于domain entity】
  • 持有自己的业务属性和业务行为 【同domain entity】
  • 一旦定义,他是不可变的,它通常是短暂的,这和java中的值对象(基本类型和String类型)类似 【区别于domain entity】
  1. domain service

领域服务。区别于应用服务,他属于业务领域层。可以认为,如果某种行为无法归类给任何实体/值对象,则就为这些行为建立相应的领域服务即可。传统意义上的util
static方法中,涉及到业务逻辑的部分,都可以考虑归入domain service。

  1. domain event

领域事件。领域中产生的一些消息事件,可以在性能和解耦层面得到好处。我们通常借助于消息中间件,通过事件通知/订阅的方式落地。

在‘社区’业务领域中,‘发帖’之后,会同时为帖子作者生成一个‘发帖动态’,这个‘生成发帖动态’场景并不同步完成,而是通过领域事件发布异步完成。‘发帖’创建Post实体后,发布一个‘发帖动态’领域事件(PostingDynamic),‘动态’(Dynamic)相关服务消费该领域事件,并生成Dynamic实体。

  1. domain factory

领域对象工厂。用于复杂领域对象的创建/重建。重建是指通过respostory加载持久化对象后,重建领域对象。

  1. repository

仓库。我们将仓库的接口定义归类在domain层,因为他和domain
entity联系紧密。仓库接口定义了和基础实施的持久化层交互契约,
完成领域对应的增删改查操作。domain层的repository只是定义契约的接口,实际实现仍然由infrastructure完成。

仓库的实际实现根据不同的存储介质而不同,可以是redis、oracle、mongodb等。
具体仓库的实现会讲给infrastructure层完成,我们会在下一篇blog中详细阐述repository的实现。

对于repository的接口定义,建议规范接口名命名,比如:查询都叫着query等等,减小沟通成本。

贫血模型;

业务逻辑都是写在Service中的,Award充其量只是个数据载体,没有任何行为。简单的业务系统采用这种贫血模型和过程化设计是没有问题的,但在业务逻辑复杂了,业务逻辑、状态会散落到在大量方法中,原本的代码意图会渐渐不明确,我们将这种情况称为由贫血症引起的失忆症。

更好的是采用领域模型的开发方式,将数据和行为封装在一起,并与现实世界中的业务对象相映射。各类具备明确的职责划分,将领域逻辑分散到领域对象中。

3. 边界划分

逻辑下沉

所谓的能力下沉,是指我们不强求一次就能设计出Domain的能力,也不需要强制要求把所有的业务功能都放到Domain层,而是采用实用主义的态度,即只对那些需要在多个场景中需要被复用的能力进行抽象下沉,而不需要复用的,就暂时放在App层的Use
Case里就好了。

注:UseCase是《架构整洁之道》里面的术语,简单理解就是响应一个Request的处理过程通过实践,我发现这种循序渐进的能力下沉策略,应该是一种更符合实际、更敏捷的方法。因为我们承认模型不是一次性设计出来的,而是迭代演化出来的。

复用性和内聚性

复用性是告诉我们When(什么时候该下沉了),即有重复代码的时候。内聚性是告诉我们How(要下沉到哪里),功能有没有内聚到恰当的实体上,有没有放到合适的层次上(因为Domain层的能力也是有两个层次的,一个是Domain
Service这是相对比较粗的粒度,另一个是Domain的Model这个是最细粒度的复用)。比如,在我们的商品域,经常需要判断一个商品是不是最小单位,是不是中包商品。像这种能力就非常有必要直接挂载在Model上。

没有领域模型,将会充斥大量if/else or equals()语句:

因为老系统中没有领域模型,没有CSPU这个实体。你会发现像判断单品是否为最小单位的逻辑是以StringUtils.equals(code,
baseCode)的形式散落在代码的各个角落。这种代码的可理解性是可想而知的,至少我在第一眼看到这个代码的时候,是完全不知道什么意思。

DDD实践

DDD 模式从天书到实践

模块

模块(Module)是 DDD 中明确提到的一种控制限界上下文的手段,在我们的工程中,一般尽量用一个模块来表示一个领域的限界上下文。

如代码中所示,一般的工程中包的组织方式为 {com.公司名.组织架构.业务.上下文.*},这样的组织结构能够明确地将一个上下文限定在包的内部。

对于模块内的组织结构,一般情况下我们是按照领域对象、领域服务、领域资源库、防腐层等组织方式定义的。

领域对象

领域驱动要解决的一个重要的问题,就是解决对象的贫血问题,而领域对象则最直接的反应了这个能力。

我们可以定义聚合根(文章)和值对象(计数器),来举例说明。聚合根持有文章的 id 和文章的计数数据,这里计数器之所以被列为值对象,而非实体的一个属性,是因为计数器是由多部分组成的,比如真实阅读量、推广阅读量等。

在文章领域对象中,我们需要定义个一个方法,来获取文章的计数量,用于页面上显示,这个逻辑可能会很复杂,涉及到爆文、专栏作者级别、发布时间等因素。

资源库

领域对象需要资源存储,资源库可以理解成 DAO,但它比 DAO 更宽泛,存储的手段可以是多样化的,常见的无非是数据库、分布式缓存、本地缓存等。资源库(Repository)的作用,就是对领域的存储和访问进行统一管理的对象。

资源库对外的整体访问由 Repository 提供,它聚合了各个资源库的数据信息,同时也承担了资源存储的逻辑(例如缓存更新机制等)。

防腐层

亦称适配层。在一个上下文中,有时需要对外部上下文进行访问,通常会引入防腐层的概念来对外部上下文的访问进行一次转义。

有以下几种情况会考虑引入防腐层:

  • 需要将外部上下文中的模型翻译成本上下文理解的模型。
  • 不同上下文之间的团队协作关系,如果是供奉者关系,建议引入防腐层,避免外部上下文变化对本上下文的侵蚀。
  • 该访问本上下文使用广泛,为了避免改动影响范围过大。

领域服务

上文中,我们将领域行为封装到领域对象中,将资源管理行为封装到资源库中,将外部上下文的交互行为封装到防腐层中。此时,我们再回过头来看领域服务时,能够发现领域服务本身所承载的职责也就更加清晰了,即就是通过串联领域对象、资源库和防腐层等一系列领域内的对象的行为,对其他上下文提供交互的接口。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 wshten@gmail.com

文章标题:领域驱动设计-对象建模

本文作者:KevinTen

发布时间:2019-11-19, 00:00:00

最后更新:2019-12-16, 21:15:18

原始链接:http://github.com/kevinten10/2019/11/19/Principle/领域驱动设计/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏

csdn zhihu github