聚合根是唯一的吗

如果出现两个领域模型,但是这两个领域模型的聚合根是相同的(比如人、书之类)。大家觉得这可能出现吗?如果出现这种情况,那么是分析设计出问题了,还是DDD出问题了?欢迎大家讨论

聚合根一般是实体,而实体的定义是标识唯一,那么聚合根应该是唯一的。

不过你的意思是,聚合根是唯一的类型?还是类型数量的唯一?比如Car有发动机 车轮 车厢高聚合组成,Car是聚合根,但是发动机也可以是聚合根,一个聚合边界内可以有几个聚合根,不过数量多操作复杂。

两个不同聚合边界分别有两个聚合根,如果他们相同,应该无所谓,根据实际情况,比如成品在进库入库子系统中是聚合根,在销售的子系统中也可能是一个聚合根。

两个子系统中运作改变的是同一个聚合根实体的不同状态而已。

我又要用这张万能图来说明了:

这张图其实说明一个聚合边界内的聚合根会发生什么?如果是两个聚合边界,还是用这张图再分析一次。在一个聚合边界内,无非是用户发出事件去改变聚合根的一些状态。


[该贴被banq于2013-01-12 08:06修改过]

  banq,其实我提出这个问题是考虑应该“如何正确地切分或设计领域边界”。因为按照“传统内聚”的思路,一个“聚合根”所有相关的逻辑都应该在一起的,那就不可能出现“2个领域模型,一个聚合根”的现象。
  如果“2个领域模型,一个聚合根”是合理的,那么在设计DDD的领域时,应该先根据业务确定领域边界,再确定”聚合根“。当然,一个领域模型内的”聚合根“应该是唯一的。但”聚合根“并不是这个领域模型存在的唯一理由,而是”场景+聚合根“才能确定一个领域模型。
  其实这里就存在了一个让我很担忧的隐患,因为每个人对实际业务”场景“的认识都是一个渐进的过程,那就可能造成领域模型的不稳定性。

[该贴被flyzb于2013-01-12 10:22修改过]

2013-01-12 10:18 "@flyzb"的内容
,因为每个人对实际业务”场景“的认识都是一个渐进的过程,那就可能造成领域模型的不稳定性 ...

是这样,特别是BDD敏捷中行为驱动开发讲究故事的渐进,这个过程实际是不断对领域模型的重构,其实DDD中强调BoundedContext也就是这个意思。

仔细分析场景是什么,场景实际主要是业务事件发生的地方,对场景认识不断加深影响到领域模型的认识,可以归结为:对业务事件的认识深入会影响到领域模型的认识。甚至不同的业务事件会作用于不太相同的领域模型上。那么需要从下面两个方面思考:

这种业务行为的改变是否需要改变领域模型的结构呢?还是只需要改变领域模型的状态呢?

归结为一句话:改变的是类型还是值?

孙悟空会72变,那么我们设计时,当孙悟空在白骨精洞这个场景变成苍蝇,那么我们是改变孙悟空这个类型还是其状态呢?

所以,我的意思是:根据具体场景来进行判断,如果发生改变类型的事情,说明我们从一开始就进入了错误的方向,角度就切入错误了。

[该贴被banq于2013-01-12 10:43修改过]

    banq,大体赞同你的观点,但细节上还有些差别。
    从你上面解释中,我发现有3个关键词:场景、事件和领域。DDD最本来的目的就是为了更好地”解决复杂场景描述问题“的。而在我看来,任何一种描述场景的设计思想都必须遵循2个基本原则:“单一性”和”复杂是简单的组合“,比如”复杂写可以分解为复杂读和简单写“、”复杂读可以分解为简单读“。
    按照这种思路,我更关注场景拆分是否合理,其中'领域的划分”和“事件”是必不可少的2个支撑“场景”拆分的关键技术。在这样过程中,我不特意强调“聚合根”,认为“敏捷DDD”只看“组合是否自然合理”。因为整个设计更强调组合,所以既使“对业务的认识有偏差”,重构更自然,成本更低。显然,重构成本是“敏捷DDD”的关键。
[该贴被flyzb于2013-01-12 12:18修改过]

2013-01-12 11:27 "@flyzb"的内容
但细节上还有些差别 ...

愿闻其详,我们以网上书店案例,你怎么看?

下完订单过段时间再去买,找到原来的订单,进入订单的商品页面,你会发现提示,这个商品已经不是最新的商品。如何进行设计呢?

是通过设置商品有不同的状态,比如最新状态,历史状态等等,还是认为订单中商品是一种值对象,而展示的商品都是最新商品,这里商品为实体,前者是通过状态来表达,后者是通过类型改变来表达。

如同孙悟空在某个上下文变成了苍蝇,那么它就是苍蝇还是处于苍蝇这个状态?

2013-01-13 11:49 "@banq"的内容
下完订单过段时间再去买,找到原来的订单,进入订单的商品页面,你会发现提示,这个商品已经不是最新的商品。如何进行设计呢?

是通过设置商品有不同的状态,比如最新状态,历史状态等等,还是认为订单中商品是一种值对象,而展示的商品都是最新商品,这里 ...

这是一个业务问题,与DDD无关。“每一笔销售出去的商品”都应该有一个明确的状态。如果是正规商品,应该有型号,或商品参数等,说明用户购买的是哪一种状态下的商品。当用户再次登录订单信息,应该看到的是当时购买所属型号的商品,而不是最新商品。当然,如果做的友好,可以推荐用户关注最新型号商品。

两个领域,一个聚合根,这明显是逻辑重叠,领域不是随便就可放在一起,这两个领域犹如相交圆,领域带来的是边界,有清晰的边界,才知道领域是否可以放到一起。

想放到一起的话,在考虑另一领域时就要对原领域进行隔离,在过去,JAVA是通过接口,界定边界。

2013-01-13 11:49 "@banq"的内容
是通过设置商品有不同的状态,比如最新状态,历史状态等等,还是认为订单中商品是一种值对象,而展示的商品都是最新商品,这里商品为实体, ...

想到了两种方法,一种是订单表中添加商品标志(历史或最新),可以在改变商品价格或商品描述时改变订单表中商品状态标志;另一种是商品表中设定一个标志值(递增数值),在改变商品价格或商品描述时改变商品表中标志值。
在用户要购买时前者查询订单状态就可知道,后者要比较订单中标志值和现有商品标志值。
不知道这两种方法是否和板桥老师的整体思路有什么对应,或者只是一种思路的不同方法而已?

好像把问题提高到了“认知”层面了,冒似这个问题的答案应该在领域专家的脑子里吧。

以下小弟瞎猜的, 不对请指正。
从聚合根是“订单”的角度看,这个“商品”是值对象吧,因为这个订单已经生命周期结束了(或者说休眠了,直到未来某时被DELETE后彻底结束), 所以这个聚合根内的所有东冬都已经已经不会再有任何改动了;由于商品是个只读对象,我认为他是个值对象。

就好像商场卖东西开发票一样,卖出的是个实实在在的商品,但是年终盘点的时候,盘点的只是发票的票根。
大概就是这个流程吧:
柜台商品开交款票->交款&给取货票->柜台凭取货票领商品

[该贴被wd1603于2013-02-04 10:31修改过]