聚合根数据重复和Demeter定律

时间:2016-05-05 02:38:31

标签: domain-driven-design cqrs aggregateroot law-of-demeter

德米特定律是一条规则,即一个物体应该只知道"密切相关的"其他对象(我的解释)。请参阅https://en.wikipedia.org/wiki/Law_of_Demeter

以下示例不遵循LoD:

// This class has to be aware of too many other classes, increasing complexity.
class Thing {
    void foo(Customer c, Employee e, System s, Company c, SupportTicket st) {
        // Th
    }
}

// This function likewise has to be aware of too many other classes.
void foo(SupportTicket st) {
    st.employee().division().incrementResolutionCount();
}

我正在研究一个在数据之间有很多复杂关系的系统。在SQL数据库中,使用四个不同的连接可以轻松执行查询以获取我想要的数据。但是,在这种情况下,您需要加载基于其实际不包含的属性的聚合。如果存储库是内存中集合的抽象(即,在最简单的用例中是数组的包装器),那么纯粹的内存中集合就不可能执行这样的查询,因为没有聚合将永远匹配它。

我想到的解决方案是你在聚合之间复制数据,而不仅仅是在有界上下文之间(http://www.infoq.com/news/2014/11/sharing-data-bounded-contexts)。这增加了对Demeter法则的一致性,同时允许您根据自然属于"属性的属性查询聚合。到相关的聚合。

推荐这种方法吗?

1 个答案:

答案 0 :(得分:11)

  

推荐这种方法吗?

不是我见过的任何当局。

  

我想到的解决方案是你在聚合之间复制数据

这对你的记录簿意味着什么?

域模型的动机是保持记录簿的完整性,确保它始终满足您的业务不变性。聚合边界描述了域内可以彼此独立更新的不相交区域 - 也就是说每个聚合都是对其自身状态的主权。

因此,当你提出一个在两个聚合之间复制数据的设计时,你真正断言的是你的记录册中的一个事实实际上是两个不同的事实,它们可能彼此独立地发展。

这可能是愚蠢的,或者可能是对业务的重要见解。一般来说,不可能争论;你必须与你的领域专家坐下来讨论它。

但是,我认为,违反德米特定律但实际上正确描述业务的域模型远远优于满足德米特定律但产生误导性业务描述的替代设计。

也就是说 是合理的,因为你的解决方案违反了LoD,并在此基础上推迟了你的要求:与你的领域专家坐下来,探索无处不在的语言,并让所有人进入关于业务是否真的那么复杂的相同页面,以及是否应该如此。

  

我正在研究一个在数据之间有很多复杂关系的系统。在SQL数据库中,使用四个不同的连接执行查询以获取我想要的数据很容易。但是,在这种情况下,您正在加载基于它实际上不包含的属性的聚合。

为什么你会通过除标识符以外的任何东西加载聚合?

坚持,你说...

  

您正在加载基于其实际上不包含的属性的聚合。

您是否尝试使用违反Demeter法则的查询来加载聚合? 吹口哨是的,有人完成了。

CQRS模式在这里可能很有用,作为帮助解开实际情况的工具。一些直接的猜测

1)如果您不想修改状态,则不需要聚合。聚合用于强制执行写入的业务约束。如果您的用例正在阅读,那么您需要一个只读的状态表示 - 也就是投影。

2)如果您正在尝试修改状态,那么您正在处理命令,并且命令消息会寻址特定的处理程序。很难想象一个用例,你将命令发送到聚合,而不能识别聚合。

另一种可能性是您正在考虑的命令实际上是一个具有订阅者的事件。通常,订阅者不会在域模型中聚合,而是通过将命令分派到聚合来对事件做出反应的流程管理器。

3)您仍有可能在错误的地方绘制了聚合边界。