时长13:19大小10.68M
作者回复: 我先理解一下你说的业务场景哈。不知道理解的对不对?不对的地方请你指出。由于不好展示事件风暴的过程,我就口述吧。
你描述的业务场景主要包括这两个部分吧。
流程一:创建任务
1、根据任务规则获取任务基础数据,生成任务。(产生任务已创建事件,领域对象包括:任务生成的基础数据、数据过滤规则、任务、任务类型)
2、自动将任务分配给销售。(销售的数据应该来源于其他系统,这个过程实际上是一个给任务赋值的过程,领域对象包括:销售)
3、销售领取任务。(给任务分配销售)
这个过程领域对象包括:基础数据、数据过滤规则、任务、任务类型、销售。
命令有:创建任务,给任务分配销售。
领域事件有:任务已创建。
流程二、任务执行
1、销售查询并获取任务,执行任务。(不清楚你说的字段在这个过程是什么含义,是查询时勾选类型吗)
2、任务执行完成后,记录执行结果,产生任务执行日志。(产生任务执行日志已创建事件,领域对象有:任务、任务日志)
3、统计日志。这一块不清楚你的业务逻辑和流程。
因为有统计日志,是不是就会去查询任务的执行日志,如果是这样的话,任务日志就需要设计为实体,跟任务关联,这里任务是聚合根。
这个阶段的领域对象有:任务和任务执行日志。
命令有:查询任务,生成任务执行日志。
领域事件有:执行任务日志已创建。
结合这两个流程,我们整体来分析一下。
领域对象包括:基础数据、数据过滤规则、任务、销售、任务类型、任务执行日志。
由于销售人员数据来源于第三方,以值的形式存储在任务中,因此我们可以将它设计为任务的值对象。
任务执行日志依附于任务,但是由于它后续要做查询和统计分析,因此将它设计为被任务引用的实体。
任务类型是任务的值对象
其它的基础数据、数据过滤规则这两个领域对象很独立,你可以理解他们是独立的实体,或者说一个实体就是一个聚合。
但是这样设计在代码目录设计时会显得比较单薄,一个聚合会有一个仓储和聚合自己的代码目录结构。因此我们可以将这两个实体可以直接放在任务的聚合里,但是他们的生命周期不受任务这个聚合根管理。
这样的话,我们就可以只建立一个任务聚合。这个聚合的聚合根是任务。它引用的实体包括任务执行日志,任务的值对象有:销售、任务类型。还有两个独立实体:基础数据和数据过滤规则。
说明一下:
在不少的数据统计和计算场景中,有很多实体之间相互独立,只参与计算和统计分析,但是这类场景中业务内聚性又很高,你找不出管理这些实体的聚合根。我称这种业务模型是非典型领域模型。虽然有些方面(比如聚合根)不符合DDD的一些原则,但是我们也可以按照DDD方法来完成设计。
作者回复: 被保人这个值对象以属性嵌入的方式嵌入保单聚合根中,查询客户保单时你不需要到客户聚合去查询客户信息了,直接根据客户信息在投保聚合查保单就可以了,当然这个客户信息不只是ID。
说明一下:这个跨聚合引用是在生成保单的时候,通过客户聚合根查询获取的客户信息,从客户聚合获取客户信息后,客户的信息就作为值对象的值嵌入到了保单实体中。
作者回复: 类比的不错。
作者回复: 因为微服务的架构演进,会有功能和代码的拆分和重构的过程。而一般来讲聚合的内聚性很高,聚合内的功能相对稳定,我们可以聚合功能和代码为单位在不同的微服务之间进行功能和代码的重构。
如果聚合之间代码和业务边界不清晰,聚合之间数据和服务可以随便访问,就会因为耦合度过高,代码很难剥离,最后微服务架构演进时又要在走一遍从单体拆分微服务的过程。后面的章节我会专门讲微服务架构的演进。
作者回复: 基础篇主要讲解DDD的基础概念和设计理念,所以相对抽象一些。后面会有中台业务建模和微服务设计案例,比较好理解。敬请期待。
作者回复: 你好,事件风暴会有专门的一节介绍。
作者回复: 值对象可以整体替换,但不能对里面的属性数据做局部修改。你可以这么理解,值对象是一个字段,但不同的是值对象里面还有有很多其它属性。数据库里面你可修改的最小单元是字段,你不可以对这个字段的具备修改,你只能整体修改字段的内容。
作者回复: 据我所知,现在的java框架对orm支持得不够好。而nosql的方式可能支持的更好一些。所以这一块的设计尽量结合自己的技术和业务场景,如果不会在聚合内产生数据不一致的情况,在技术不具备的时候,咱们没必要为了DDD而做DDD。
作者回复: 限界上下文如果被被设计为一个微服务的话,它的代码就是一个微服务发布包。理论上一个限界上下文就可以设计为一个微服务,但它还受其它外部因素限制,比如技术异构,团队沟通成本,版本发布频率,性能等因素。
聚会根就是一个实体。不过它是一个具有管理功能的特殊实体。
作者回复: 应用服务是在聚合之上的,它不是单独属于某个聚合的。在应用服务内聚合根可以引用其它聚合的聚合根,读取或者修改其它聚合的实体。
作者回复: 1、聚合根主要协调聚合内的实体和值对象,通过引用的方式是从数据的角度来保证数据的一致性。而领域服务主要是从业务行为,通过实体属性和实体方法来进行业务逻辑的组合和编排,多个实体协作完成复杂的业务逻辑。
2、客户信息修改后,是不能影响保单中的客户数据的,这个客户数据是跟单数据,不会随着客户实体数据的变更而变化。举个收货地址的例子,你在维护你个人中心的多个收货地址后,不会修改已经发货的订单上的地址一样的。除非你提出变更收货地址。
3、一般来说应用层不应该有自己的实体对象,它引用领域层的对象,不实现复杂的业务逻辑。通过对不同聚合的领域服务组合和编排,实现跨聚合的业务协作。
4、在微服务内只有一个应用层。在微服务内尽量避免聚合之间的直接交互。聚合之间的交互都通过应用层。如果设计时将某个聚合放在了不合适的限界上下文内,以后在聚合拆分和合并时,由于聚合之间耦合度低,微服务的演进也就容易的多。
作者回复: 是的,感觉现在JAVA的ORM框架还不是强大。
作者回复: 是的。
作者回复: 聚合还是需要的,在后面的代码目录结构了,我对聚合专门设计了一个目录结构,主要目的是起到功能和代码聚合的作用,以后微服务架构演进就相对方便。
至于聚合根,如果聚合内数据规则简单,不会产生数据不一致的情况。如果通过聚合根会影响到使用的便利性,比如性能效率等,我个人觉得也可以不设计聚合根,采用传统的设计方法也是没有关系的。一切以解决问题为基本要求,不要为了DDD而做DDD。
作者回复: 应用服务主要做服务组合和编排,领域服务实现多个实体的核心业务逻辑。这一块在后面的章节他们是主角,会有详细介绍。请耐心等待。
作者回复: 聚合也别太小了,一个实体设计成一个聚合,就没啥意义了。还是要考虑业务内聚性,尽量实现实体的归类。以后微服务的架构演进还要以聚合为单位来演进呢。
作者回复: 后面章节中的设计会详细到包名,类名和服务等这些微服务代码目录结构。请耐心等待哈。
作者回复: 是的,在一个聚合内是由聚合根来管理和协调所有实体和值对象的生命周期的,所以在一个聚合内可以保证数据的一致性。
由于多个聚合运行在同一个微服务内,在应用层我们是可以拿到其它聚合的聚合根的DO对象的,拿到聚合根后你就可以通过聚合根引用聚合的内部实体和值对象。
作者回复: 应该是电商领域下的三个不同的子域。
你可以对这三个子域进行事件风暴,找出实体和聚合,划分限界上下文,建立领域模型,完成微服务设计。
作者回复: 说到充血模型,就离不开贫血模型。
先说一下贫血模型吧,贫血模型是指使用的领域对象中只有setter和getter方法,所有的业务逻辑都不包含在领域对象中而是放在业务逻辑层。
而充血模型将大多数业务逻辑放在领域实体中实现,实体本身包含了属性和它的业务行为,它在领域模型中就是一个具有业务行为和逻辑的基本业务单元。