我面临的问题要么无法应用DDD,要么不能重新重新制定案件以适合DDD规则。
要求:
- 我们有
产品
和位置
。
- 更改
位置
应反映在 product
然后看到的。因此,产品
参考位置
。
- 相同的
位置
可以在几个产品
中使用。
-
产品
可以在任何时间点创建和删除。
-
位置
可以在任何时间点创建。
-
位置
只有在没有产品
引用此位置
的情况下才能删除。
-
product
的删除不会导致位置
删除。
该模型中还有两个实体,具有与位置
相同的规则,但是解决方案应与 location
案例相同。
澄清1 :
product
在不在任何位置
的情况下,也不能与任何 location> location
,它必须有一个位置。您可以在任何时间点创建两个聚合物,但是创建 product
的条件已经存在位置
。
我的想法:
因此,我看到的问题是,这种要求会产生循环依赖性 product
和位置
。因为他们都有自己的无限生命周期,所以他们应该是单独的聚合根源,但是这违反了一致性边界,因为基本上一个AR试图控制另一个AR。因此,这个不变的人不能放在这两个中的任何一个中。试图将其提取到顶部AR上的第三个也不清楚这将如何工作。
问题:
如何满足DDD中的此类要求?删除约束6不是一个选择。我已经尝试与业务方面谈判这一点。
I'm facing the issue where either I can't apply DDD, or the case can't be reformulated to fit the DDD rules.
The requirements:
- We have a
Product
and Location
.
- Change in
Location
should be reflected in Product
which user then sees. Therefore Product
references Location
.
- Same
Location
might be used in several Products
.
Product
might be created and deleted at any time point.
Location
might be created at any time point.
Location
might be deleted only if there is no Product
references this Location
.
- Delete of a
Product
does not lead to Location
deletion.
There are two more entities in the model exist with the same rules as Location
, but solution should be the same as with Location
case.
Clarification 1:
Product
can't exist without being in any location
, or without being related to any location
, it MUST have a location. You might create both aggregates in any time point, but condition to create Product
is having a Location
already exist.
My thoughts:
So the problem which I see, that such requirements creates circular dependency on Product
and Location
. Because they all have own unlimited life cycle they supposed to be separate aggregation roots, but then this violates the consistency boundary, as basically one AR is trying to control another AR. So, this invariant can't be placed in any of those two. Trying to extract this into third one on the top AR also not clear how this would work.
The question:
How can I satisfy such requirements in DDD? Removing the constraint 6 is not an option. I've already tried to negotiate this with business side.
发布评论
评论(4)
聚合是一致性边界。因此,如果您绝对需要对任何位置的更改才能立即反映在产品中,反之亦然,那么您基本上需要将所有位置和所有产品用于一个汇总,然后具有某种形式的并发控制形式,这有效地限制了该巨型 - 汇总变化(并将大大限制该大型聚集可以改变的速率)。
请注意,您可以使用基础架构(例如具有外国密钥要求的关系DB交易)来假装真正的一个大型聚集物是多个较小的聚集体。这样做不会改变基本的性能(它的上限可能比简单的DDD实现将使用的钥匙值乐观的并发控制方法更高,因为它将允许并发更新以散射大型杂物的部分)并且有一个缺点,而不是您的域逻辑居住在代码中的一个位置,现在已在您的DB模式中部分编码(无论它是否在代码中的其他位置重复)。
说不能应用DDD(特别是聚集体)或案例不符合骨料周围的规则,这并不是真正准确的,不止一个人可以说不能应用物理定律。只是您在应用要求时获得的解决方案不是您喜欢的。
或者,需求可能是互不兼容的(例如,要求能够在某个时期内执行X更新并具有很强的一致性)。在这里,炼油要求可能会有所帮助,特别是涉及松动一致性要求。比域实际要求更强大的一致性要求确实很普遍(询问“如果发生这种情况……实际发生的事情……”是有帮助的)。
Aggregates are consistency boundaries. So if you absolutely need changes to any location to be immediately reflected in products, and vice versa, then you basically need to have all locations and all products in a single aggregate and then have some form of concurrency control which effectively limits how that mega-aggregate changes (and will dramatically limit the rate at which this mega-aggregate can change).
Note that you can use infrastructure (e.g. relational DB transactions with foreign key requirements) to pretend that what's really one mega-aggregate is multiple smaller aggregates. Going this way doesn't change the fundamental performance (it's likely to have a much higher ceiling than the key-value optimistic concurrency control approach that a simple DDD implementation will use because it will allow concurrent updates to disparate parts of the mega-aggregate) and has the downside of instead of your domain logic living in one place in your code, it's now partially encoded in your DB schema (whether or not it's duplicated elsewhere in your code).
It's not really accurate to say that DDD (specifically aggregates) can't be applied or that a case doesn't fit the rules around aggregates, any more than one can say that the laws of physics can't be applied. It's just that the solution you get to when applying the requirements is not one you like.
Or it's possible that the requirements are mutually incompatible (e.g. a requirement to be able to perform X updates in some period of time with strong consistency). This is where refining requirements may be helpful, specifically around loosening consistency requirements. It's really common for a stronger consistency requirement to be assumed than the domain actually requires (asking questions like, "what actually happens if this happens..." is helpful).
这一切看起来都很简单。您的“产品”数据模型包括一个链接(名称,标识符等)到“位置”。
唯一棘手的位是:
您可能应该查看Udi Dahan:不要删除 - 只删除 - t 。
重要问题:这里失败的业务成本是多少?
如果拥有具有删除位置的产品很昂贵,那么您必须考虑到该限制的数据模型。这可能意味着您在删除位置时必须锁定产品数据,并且在更新产品时也可能锁定位置。
猜测,您可能会考虑将所有产品和位置存储在同一数据库中,并设置外键约束以确保遵守规则。是的,由于这种设计,可能会受到绩效罚款,但这是因为它解决了昂贵的问题而证明了这一点。
另一方面,如果业务更加宽容,那么可能还有其他思考域模型的方法。一种可能性是,位置具有“状态” - 处于活动状态或弃用,如果不允许产品处理产品为弃用的位置分配产品(弃用的位置将在下拉列表中隐藏),但是弃用的位置仍然出现在报告中。
(实际上,我们在这里谈论的一种软删除形式,但是在我们的实际业务流程层面上,而不是在管道中。)
这可能有助于扩展您对业务中“位置”的理解 - IS位置一个概念,该领域模型是真理的来源?还是位置是参考数据的形式?位置是由一些数据导入 /数据输入过程引起的,还是您的域模型产生的。
在原始DDD书中的示例,位置确实是参考数据;国际端口和终端的实际清单是由UN/ECE管理,更新两次。一年。
因此,在对运输域进行建模时,我们期望位置代码通过数据导入过程出现。请注意,这里的“删除”内置了很多延迟 - 国家当局决定应将地点划清位置,并且需要由联合国/ECE处理,这意味着下一个出现的问题报告说明了代码。 标记为删除;并制定了各种流程,以确保“删除”后,相关代码在一段时间内不会重复使用。
鉴于信息在系统中移动需要几个月的时间,这里的任何“直接一致性”要求都是荒谬的。域模型中必须有空间,以便在不等待其他地方做出的决定的情况下做出本地决策。
另一方面,如果您要为联合国/ECE创建一个位置模型,这本身就是代码分配的真实来源,那么更严格的一致性要求可能是合适的。
马课程。
This all looks pretty straight forward; your data model for "product" includes a link (a name, an identifier, etc) to a "location".
The only tricky bit is:
You should probably review Udi Dahan: Don't Delete - Just Don't.
Important question: what's the cost to the business of a failure here?
If having products with deleted locations is expensive, then you have to design your data model with that constraint in mind. That probably means you are going to have to lock your product data when deleting locations, and maybe also locking locations when updating products.
At a guess, you're probably going to be looking at storing all products and locations in the same database, and setting up foreign key constraints to ensure that the rules are followed. Yes, there might be a performance penalty because of this sort of design, but that's justified by the fact that it solves an expensive problem.
On the other hand, if the business is a bit more forgiving, then there may be other ways of thinking about the domain model. One possibility is that Location has "state" - being either active or deprecated, where product processing is not permitted to assign products to deprecated locations (deprecated locations would be hidden from the dropdown), but the deprecated locations still appear in reports.
(In effect, we're talking about a form of soft delete here, but at the level of our actual business processes, rather than down in the plumbing.)
It might help to expand your understanding of "Location" in the business - is Location a concept where this domain model is the Source Of Truth? or are instead Locations a form of reference data? Do locations arise out of some data import / data entry process, or are they something produced by your domain model.
In the Cargo Shipping example from the original DDD book, locations are really reference data; the actual list of international ports and terminals is managed by UN/ECE, with updates being issued twice a year.
Therefore, in modeling a shipping domain, we'd be expecting location codes to appear via a data import process. Note that "delete" here has a lot of latency built into it - the national authority decides a location should be delisted, and that needs to be processed by UN/ECE, and that means that the next issue that goes out reports that the code is marked for deletion; and various processes in place to ensure that the code in question is not re-used for some extended period of time after being "deleted".
Any sort of "immediate consistency" requirement here would be absurd, given that it takes months for the information to move through the system. There has to be room in the domain model for local decisions to be made without waiting on decisions made elsewhere.
On the other hand, if you are creating a location model for UN/ECE, which is itself the Source of Truth for code assignments, then stricter consistency requirements might be appropriate.
Horses for courses.
在您的点2中,您说
我认为这个结论是不正确的。我的看法是,该产品不需要了解位置,或者至少您没有提到任何需要的规则。用户需要查看产品位置的事实意味着您需要一种基于产品ID查询位置的方法,但这是一个查询问题,而不是汇总问题。如果您遵循CQRS,则汇总与查询区别将显而易见。
您可以设计一个可以保留产品集合的位置聚合。这些产品在该位置汇总的产品不是实际的产品聚集体,而是具有生产力以及潜在其他属性的实体。
让我们看一下您的规则:
通过在该位置集合中拥有一系列产品来解决这一问题
产品可以不在任何位置而存在。
删除产品后,您需要发布一个事件,以便所有包含该产品的位置都可以将其删除(这也需要一种查找引用Productids的位置骨料的方法)
没问题。可以在不引用任何产品的情况下存在一个位置。
现在,这很容易实现,因为删除位置在集合可以检查其产品集合中的操作。
这不是问题,因为产品甚至都不知道
该设计的问题是,从技术上讲,可以将相同的产品添加到多个位置。我不知道这是否是一个问题。一种可能的解决方案是,当产品被添加到一个位置时,位置汇总发布了一个已经包含该产品的位置将处理并自动从集合中删除该产品的事件。
您还可以使用技术解决方案避免这种情况,例如
locationproducts
表中的DB中的唯一密钥约束。In your point number 2, you say
I don't think this conclusion is correct. The way I see it, the Product does not need to know about locations, or at least, you haven't mentioned any rule that requires that. The fact that the user needs to see the Product location means that you need a way to query locations based on Product Ids, but that's a query issue, not an aggregate issue. The aggregate vs query distinction becomes apparent if you follow CQRS.
You can design a Location Aggregate that keeps a collection of Products. These products in the Location aggregate won't be actual Product Aggregates, but entities with a ProductId plus potentially other properties.
Let's look at your rules:
This is solved by having a collection of Products in the Location aggregate
A Product can exist without being in any location.
When a product is deleted, you'll need to publish an event so that all locations that contain that product can remove it (that would also require a way of finding Location aggregates referencing ProductIds)
No problem. A Location can exist without referencing any products.
This is now easy to implement as the Delete operation in Location aggregate can check its Products collection.
Not a problem, as Product doesn't even know about Locations
The issue with this design is that it's technically possible to add the same Product to multiple locations. I don't know if that is an issue or not. One possible solution is, when a Product is added to a Location, the Location aggregate publishes an event that the Location already containing that product will handle and automatically remove that product from the collection.
You could also avoid this scenario with a technical solution, like a unique key constraint in the DB in the
LocationProducts
table.正如您正确说明的那样,
产品
和位置
是两个不同的聚合。他们不能互相控制,并且拥有product
保留对单个位置
的引用没有问题。在位置
中,持有products
的列表(甚至简单的计数器)也是如此。现在,假设您担心演示层(例如A UI甚至API),例如,由于可能更改
位置
,或删除产品
。在这种情况下,您可能需要考虑派遣域事件并编写适当的处理程序来处理它们。您可能还需要考虑使用CQR,并创建/刷新查询模型将被演示层消费。As you have correctly stated,
Product
andLocation
are two distinct aggregates. They don't control each other, and there is no problem in havingProduct
holding a reference to a singleLocation
. The same goes for holding a list (or even a simple counter) ofProducts
inside aLocation
.Now, suppose you are concerned about the Presentation Layer (eg. a UI or even an API), for example, due to potential changes to a
Location
, or deletion of aProduct
. In that case, you might want to consider dispatching Domain Events and write the appropriate handlers to deal with them. You might also want to consider using CQRS, and create/refresh Query Models that will be consumed by the Presentation Layer.