本文介绍了DDD-如何补水的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们有一个带有私有设置者的汇总根,但是公众获取者访问 state

Let's say we have an Aggregate Root with private setters but public getters for accessing state

行为是通过汇总根上的方法完成的。

Behaviour is done through methods on the aggregate root.

指示存储库加载

此刻,我看到几种可行的方法:

At the moment I see a couple of possible ways to achieve this:


  1. 通过反射(手动或自动,例如
    Automapper)设置状态

  2. 使构造函数接受属性,以便声明设置

  3. 使用

  1. set the state through reflection (manual or automatic eg.Automapper)
  2. make constructors that accept properties so state is set
  3. load the aggregate with a state object

1)暗示他的工具 Automapper 并不意味着双向映射。但是有些人认为我们必须务实,以能够帮助您的方式使用工具。

1) Jimmy Bogard alludes that his tool Automapper isn't meant for two-way mapping. But some people argue that we have to be pragmatic, use tools in a way it helps you.

对我来说,我不喜欢完整通过反射补液。可能存在Automapper Ceise或弯曲了聚合根,从而可以完成映射(请参见在他的文章中)。

For me, I don't like a full rehydration through reflection. Maybe Automapper ceise to exist or aggregate roots are bent in such a way the mapping can be done (see some comments of Vaughn on his article).

2)创建用于补充水分的构造子,并带有几个参数,以便使骨料的状态得以补充水分

2) creating constructors for rehydration, with a couple of parameters so the state of the aggregate is rehydrated in a correct way.

这两个参数可以扩展(=新构造函数)或定义可以更改。除了部分参数之外,我喜欢这种方法。

These couple of parameters can expand (= new constructors) or the definition can change. I like this approach, except the part of having a bunch of parameters.

3)状态是聚合根的属性。状态封装在一个新对象中,该对象由存储库构建,然后提供给聚合根以进行正确的初始化。

3) the state is a property of the aggregate root. The state is encapsulated in a new object and this object is build by the repository and is then given to the aggregate root for proper init.

有人认为构建此状态对象是另外的工作(新类,在实体上公开状态属性以及汇总根以执行业务规则) ),但它提供了一种初始化状态的干净方法。

Some people argue that building this state object is more work (new class, exposure of state properties on entity and aggregate root to enforce business rules), but it provides a clean way to initiliaze the state.

说我们需要事件源,状态的加载与加载事件类似吗?状态对象是否提供处理事件的方式?

Say that we need event sourcing, does the loading of a state resemble in loading events? And does the state object provide a way of handling events? Is it more future proof?

推荐答案

我认为尝试过分证明未来会导致很多人陷入困境这给代码库增加了不必要的复杂性。在合理的架构决策与过度架构无法保证存在的问题的解决方案之间可以达到很好的平衡。

I would argue that trying to future-proof too much represents a trap that many people fall into that adds undue complexity to a codebase. There is a fine balancing act between sound architectural decisions and over-architecting a solution to problem that is not guaranteed to exist.

话虽如此,我完全同意Jimmy的观点。说,关于AutoMapper不打算用于双向映射。您的域表示应用程序中的真相,并且不应直接可变。我从事的项目具有双向映射,尽管它们确实起作用,但有一种趋势开始将域对象仅当作DTO对待。当您开始拥有只读属性时,必须进行反射以进行设置(无论是否使用工具),这会变得很痛苦。从DDD的角度来看,我们不应该让外部影响简单地说出财产价值,因为随着时间的推移,它很可能导致贫血领域模型。

That being said, I fully agree with what Jimmy says, in regards to AutoMapper not being intended for two-way mapping. Your domain represents the "truth" in your application, and should not be directly mutable. I have worked on projects with two-way mappings, and while they do work, there is a tendency to start treating the domain objects as nothing more than DTOs. It becomes painful when you start having read-only properties, having to reflect to do your setting - tooling or not. From a DDD perspective, we should not be allowing for outside influences to simply say what a property value should be, because it will lead to an anemic domain model over time, most likely.

内部状态可以很好地工作,但是它们以额外的开销和复杂性为代价。如您所述,这是一个合理的折衷,因为您要添加大量的工作。但是,您可以利用此机会允许聚合在允许设置状态之前根据聚合中的独立业务规则验证状态。这解决了我对双向映射的最大关注。您至少可以强制状态对象包含有效数据,然后仅在有效时构造聚合。它也是可测试的。我用这种方法看到的最大问题是,您团队的技能水平将与正确利用此方法的成功直接相关。有人可能会争辩说,复杂性并没有增加足够的价值来实现整个域的范围,因为您可能会拥有具有不同流失级别的聚合。我参与的几个项目都使用了这种方法,但与直接使用构造函数相比,我发现没有什么优势。

Internal states do work well, but they are at the cost of additional overhead and complexity. There is a legitimate trade-off, as you mention, in that you are adding a fair amount of work. However, you can use that opportunity to allow the aggregate to validate the state against the self-contained business rules within the aggregate, prior to allowing the state to be set. That addresses the largest concern that I have with two-way mapping. You can at least enforce that a state object contains valid data and then only construct the aggregate if it is valid. It is more testable, as well. The largest problem that I have seen with this approach is that the skill level of your team will have a direct bearing on the success of this being utilized correctly. It could be argued that the complexity does not add enough value to implement domain-wide, as you will likely have aggregates that have different levels of churn. A couple of projects that I have been involved in have used this approach, and I found little advantage over straight constructor usage.

通常,在大多数情况下,我使用构造函数进行补液。它在不过于复杂之间走开了界限,再加上对聚合的责任是允许还是不允许对象的构造-再次,允许域控制水化尝试是否会导致有效的对象。构造函数膨胀的一个很好的折衷方法是使用可变的DTO作为构造函数的参数,本质上充当数据结构,以随着时间的推移保持一致的构造函数签名。从本质上讲,它在某种程度上也可以面向未来。它采用了状态对象方法最吸引人的优点,即干净的签名,但删除了内部抽象的附加层。

Normally, I use constructors for rehydration in most cases. It walks the line between not being overly-complex, plus it leaves responsibility for the aggregate to allow or disallow the construction of the object - again, allowing for the domain to be in control of whether the hydration attempt would result in a valid object. A good compromise to constructor bloat is the use a mutable DTO as a parameter for the constructor, essentially acting as a data structure to maintain a consistent constructor signature over time. In that essence, it is also somewhat future-proof. It takes the most attractive perk of the state object approach, which is the clean signatures, but removes the additional layer of an internal abstraction.

您提到事件源是一种可能在路上。状态加载与您将要执行的操作完全不太相似(我认为)。使用状态对象,您可以在给定的时间点快照聚合的状态。使用事件源,您将重播事件,每个事件代表改变状态(相对于状态本身)所需的数据。这样,您的构造函数很可能是事件的集合,代表一连串的增量来重复改变状态,直到到达当前状态。当您想给聚合添加水合物时,将向其提供与该聚合相关的事件,它将重播它们以达到当前状态。这也是事件源的真正优势之一。您每次都在强制您的域对象进行水合处理以创建它们所需的业务逻辑。给定事件列表,聚合将通过以一致的方式应用事件(无论是实时应用事件还是回放到当前状态)来强制执行每个状态更改都是有效的。

You mention event sourcing as a possibility down the road. State loading is not very similar to what you would be doing, at all (in my opinion). With a state object, you are snapshotting the state of the aggregate at a given point in time. With event sourcing, you will be replaying events, each of which represents the data required to mutate the state, as opposed to the state, itself. As such, your constructor will likely be a collection of events, representing a chain of deltas to mutate the state repeatedly, until it reaches the current state. When you want to hydrate your aggregate, you will supply it with the events that are related to that aggregate, and it will replay them to get to the current state. This is one of the true strengths of event sourcing, as well. You are forcing the hydration of your domain objects to go through the business logic required to create them, each time. Given a list of events, the aggregate will enforce that each state change is valid by applying the event in a consistent fashion, whether the event is being applied in real-time, or replayed to get to the current state.

回到面向事件的方面,因为它与事件源有关,所以当事件需要更改时,需要有意识的努力。由于您必须重播事件才能回到当前状态,因此您很可能必须弃用事件并提出新事件以随着业务逻辑的变化而过渡到该事件。您可能(读作可能会)发现自己的版本控制事件。您的集合不仅需要了解当前的状态更改要求,而且还需要了解以前的状态更改要求。因此,如果您更改事件处理程序,则必须确保它也对现有事件有效。当您向事件添加其他数据时,通常不会涉及太多数据。但是,当您开始从事件签名中删除数据时,您会立即使该事件面临与早期结构不兼容的风险。同样,即使更改事件内部数据结构的名称也可能导致向后兼容问题。如果您开始事件源,那么与向后兼容性一样,您不必担心将来的问题。事件源非常好,但要准备增加其他复杂性。

Back to the future-proof aspect, as it relates to event sourcing, there is a conscious effort required when events require change. Since you have to replay an event to get to the current state, you will very likely have to deprecate events and bring up new events to transition to as your business logic changes. You may (read as "likely will") find yourself versioning events. Not only does your aggregate need to understand current state change requirements, but it also needs to understand previous state change requirements. So, if you change an event handler, you will have to ensure that it will be valid for existing events, as well. When you are adding additional data to an event, it is usually not too involved. But when you start removing data from an event signature, you instantly make that event at risk for being incompatible with earlier structures. Likewise, even changing the names of the data structures inside of an event can cause backwards compatibility issues. If you start event sourcing, you do not need to worry as much about future-proofing as you do backwards compatibility. Event sourcing is great, but be prepared for additional complexity.

这篇关于DDD-如何补水的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-12 19:33