DDD:从聚合到函数组合的改变


来自OSKAR DUDYCZ的DDD变化旅程。

这是我目前所处的进化阶段:
我从经典聚合开始,遵循领域驱动设计和典型的面向对象战术模式。因此,将数据和行为封装在一个类中。然后,仅允许通过公共方法进行更改,并仅以只读模式公开数据。

这种方式促使我更多地思考数据而不是业务逻辑。很难推理这个聚合实际上对数据和行为的混合做了什么。

通过与其他人的讨论,我也发现我并不是唯一一个这样的人。

我看到了面向对象方式封装的价值,但“类”并不是实现这一点的唯一方法。

我开始研究如何更好地进行建模并将其反映在代码中。因此,对简单性的追求已经开始。

与此同时,我花了很多时间进行和学习事件溯源。这产生了明显的影响。我慢慢意识到经典的面向对象风格,尤其是 C# 和 Java 风格,与我们在事件溯源流中所拥有的不匹配。

您可以将事件流视为故事。在一个好的故事中,主角会发生转变;事件溯源中的实体状态机类似主角,每个状态实际上是一个不同的聚合,它具有一组相似的数据,但行为不同。

统一的、经典的 C#/Java 版本对我来说变得越来越过时。

近年来,我也在 TypeScript 方面做了很多工作,这让我对代数类型的编码有了另一种视角。

对于大多数人来说,数据+函数更容易理解,但是这会造成贫血领域模型?我不认为代码结构会让模型变得失血。如果我们不反映其中的商业模式和行为,那么它就会变得失血。

从我的角度来看,实体+函数比我看到的许多面向对象的实现要丰富得多。
小块的组合是控制复杂性和认知负荷的唯一方法。

最后但并非最不重要的一点是,使用不可变结构使我的代码更容易被信任。我知道它们不会被修改,如果对象被创建,那么它已经被验证了,我不需要重复 IF/验证等。

banq注:聚合是事件流中的一个拍照片段,如同用相机拍摄生活,你只能抓取某个时间点静止的画面。
事件溯源的问题在于忽视了BC上下文限制这个概念,在事件溯源中是没有BC的,因为事件本身一个个排列起来组成上下文。
而聚合是与上下文BC有关,类似拍照,何时拍照是根据当时上下文的,一张照片的特色主要取决于你当时抓拍的时机很独特,这是业务系统的独特魅力。
当然,因为独特,随着时间转移,业务内容不断变化,你拍照的上下文不同了,那么当时构建的独特业务系统也就失去魅力了。这时候只能重新抓拍,也就是重新根据新上下文建立聚合模型。

事件溯源则是完全回避了内容的抓拍和摄取,而是试图从完全形式上提供一劳永逸的方式,这种思路与分布式事务是一致的,将形式与状态内容区分开来。无状态与有状态分离,这是值得探索。
但是,从实用角度考虑,如果不想过度设计,那么就得勤快点,勤劳的人是最讨厌形式主义的。