共计 2235 个字符,预计需要花费 6 分钟才能阅读完成。
Aggregate
在一个明确的,有限的边界内,把实体、值对象、其他等资源组合在一起,就成了一个聚合。
聚合是针对一个相关对象的数据变化。每个聚合都有一个根,这个根就是一个实体,并且它是外部可以访问这个聚合的唯一对象。根可以保持对任意聚合对象的引用,并且其他的对象可以持有任意其他的对象。但是外部对象只能持有根对象的引用。
聚合和聚合根
为什么要叫聚合根,而不是聚合?如果聚合不是派生于实体,那么聚合对象就没有边界,没有边界,还怎么管理?聚合一定是派生自实体的,所以叫聚合根,并且使用了其他的实体、值对象,当然也可以使用其他的聚合根。这样设计的好处是可以通过根实体来做边界的选择组合。通常聚合根内是强一致的事务处理,多聚合之间是最终一致的事务处理。
为什么要在实体和界限上下文之间增加聚合和聚合根?
让实体和值对象协同工作,在实现公共业务逻辑的时候,可以保证数据的一致性;
聚合的设计原则:高内聚,聚合尽量小,聚合之间通过id关联,边界之外使用最终一致性,在应用层实现跨聚合的调用。
如何设计聚合?
-
通过事件风暴(用例分析,场景分析,用户旅程分析)得到实体和值对象,然后找出聚合根;
-
按照高内聚低耦合的设计原则,找出跟聚合根紧密关联的实体和值对象,即形成聚合
-
画出聚合内的实体和值对象的引用依赖关系;
-
最后把业务关联紧密的聚合画在同一个限界上线文中,即完成了领域建模;
示例
以经典的订单支付的场景为例。
订单支付需要涉及两个业务操作:
-
更改订单状态
-
扣减用户余额
故订单支付的聚合根是派生于订单实体,关联了用户实体,才有了支付行为。
// OrderPayRoot 订单支付聚合根
type OrderPayRoot struct {
entity.Order // 订单实体
userEntity *entity.User. // 用户实体
orderRepo dependency.OrderRepo
userRepo dependency.UserRepo
tx *domainevent.EventTransaction
}
// Pay 订单支付
func (root *OrderPayRoot) Pay() error {
......
}
工厂
实体和聚合通常会很大很复杂,尤其是聚合根。实际上通过构造器努力构建一个复杂的聚合也与领域本身通常做的事情相冲突。
而在领域设计中,事物的创建通常由其他的事物来创建。
因此,有必要引入一个新的概念,就是工厂(Factory)。工厂用来封装对象创建所必需的原材料,对创建聚合特别有用。当聚合的根建立时,所有聚合包含的对象随之建立,所有的不变量得到了强化。
所以订单支付聚合根,订单发货聚合根等,都可以通过订单聚合根工厂来生产。
packge aggrete
import (
...
)
func init() {
dt.Prepare(func(initiator dt.Initiator) {
initiator.BindFactory(func() *OrderFactory {
return &OrderFactory{}
})
})
}
// OrderFactory 订单聚合根抽象工厂
type OrderFactory struct {
Worker dt.Worker
TX *domainevent.EventTransaction
orderRepo dependency.OrderRepo
userRepo dependency.UserRepo
}
// NewOrderPayRoot 创建订单支付聚合根
func (factory *OrderFactory) NewOrderPayRoot(orderNo, userID int) (*OrderPayRoot, error) {
orderEntity, err := factory.OrderRepo.Find(orderNo)
if err != nil {
return nil, err
}
userEntity, err := factory.UserRepo.Get(userID)
if err != nil {
return nil, err
}
root := &OrderPayRoot {
Order: *orderEntity,
userEntity: userEntity,
orderRepo: factory.OrderRepo,
userRepo: factory.UserRepo,
tx: factory.TX,
}
return root, nil
}
在领域服务里的使用
// OrderService 订单领域服务.
type OrderService struct {
Worker dt.Worker //运行时,一个请求绑定一个运行时
OrderRepo dependency.OrderRepo //依赖倒置订单资源库
OrderFactory *aggregate.OrderFactory //依赖注入订单工厂
}
// Pay 订单支付 .
func (o *OrderService) Pay(orderNo string, userID int) (e error) {
root, e := o.OrderFactory.NewOrderPayRoot(orderNo, userID)
if e != nil {
return
}
return root.Pay()
}