我想做一些练习并将 DDD 应用于应用于 Northwind 数据库的域模型。即使 Northwind 是一个例子,我想它也是为了满足一些“虚拟业务”的需求。因此,我们的目标是拥有一个尊重 DDD 的领域模型,并且数据存储在 Northiwnd 数据库中。
考虑这个 EF 持久化模型:
(来源:developpeur.org)
请注意我们只有实体和双向关系。我想要一个真正尊重 DDD 的 DM。此外,我的 DM 模型不需要是我的数据库的镜像
如何在需要时将 des 关系更改为仅具有一种方式关系或两种方式。
-
是否有任何多对一或一对多关系可以更改为一对一?
-
您将如何为聚合建模?
-
如果需要,值对象、服务和工厂怎么样?
我知道我可能应该查看业务需求以及模型应如何更改,但希望得到您的建议。
如果我的问题不清楚,请随时询问详细信息。
提前致谢。
I would like to do some exercice and apply DDD to my Domain Model applied to Northwind database. Even if Northwind is an example I imagine that it was done to satisfy some " virtual business" requirements. So the goal is to have a Domain Model that respects DDD and the data is stored in Northiwnd database.
Consider this EF persistance model :
(source: developpeur.org)
Notice that we have only the entities and two way relations. I would like to have a real DM which respects DDD. More, my DM model doesn't need to be the mirror of my database
-
How you would change des relations to have only one way relations or two ways when needed.
-
Are there any many-to-one or one-to-many relations that could be changed to one to one?
-
How would you model agregates ?
-
How about Values Objects, services and factories if needed ?
I know that probably I shoul look at business requirements and that look how the model should change but would like to have your advaice on that.
Don't hesitate to ask for details if my question is not clear.
Thanks in advance.
发布评论
评论(1)
一般来说,我很想回答 Mu (在禅宗意义上),因为从 DDD 的角度来看,这种情况是错误的。在 DDD 中,我们从业务需求和领域专家开始,并从中导出领域模型。
尽管这是一个有争议的观点,但数据库几乎只是业务需求的附带产物(几乎总是声明实体必须持久化)。
也就是说,我会尽力做到最好。
在大多数情况下,订单是一个非常重要的业务对象,显然我们需要了解订单行,包括每行中的产品,因此看起来我们需要从订单行(Order_Detail)到产品的关联。
然而,当给出一种特定产品时,我们很少需要知道它包含在哪些订单中,因此这表明存在一种单向关系。我们可以从订单行导航到产品,但不能从产品导航到订单行。
然而,从更深层次上看,上述分析可能是错误的。想象一下开发人员和领域专家之间的以下对话:
开发人员: 我们创建了从订单到产品的这种关联,以便我们始终了解特定订单中产品的所有信息。
Exp: 听起来不错,但是供应商呢?
开发: 这也包括在内。
Exp: 那么当我们更改产品的供应商时会发生什么?
Dev: 这将隐式反映在订单数据中:好吧...
Exp: 这不是我们想要的。我们希望数据反映发货时的订单。
在这种情况下,事实证明数据库架构中实际上存在错误。该问题可能是由报告引起的,因为业务分析人员可能想要运行有关不同供应商如何影响收益的报告(我知道什么)。
这种情况可能建议完全切断从订单行到产品的关联。订单应保存历史(快照)产品数据,而不是当前产品数据。
我们甚至可以引入一个在语义上镜像
Product
Entity 的ProductSnapshot
Value Object 来在域模型中对此进行建模。总而言之,将
Order
建模为自身和订单行(使用ProductSnapShots
)的聚合似乎越来越合理,但是订单和客户之间的关系又如何呢?据我目前了解关联和聚合,关联定义聚合。收到订单后,我们想了解客户的情况吗?最有可能的。给定一个客户,您想了解订单情况吗?大概。
这表明了一种双向关系,使
Customer
成为聚合根。这意味着您将拥有CustomerRepository
,但没有OrderRepository
。每次您需要订单时,都必须通过客户
获取。在某些情况下,这可能非常有意义,而在其他情况下这可能非常笨拙。这实际上取决于业务需求...
您也可以考虑创建一个
OrderRepository
,但这会侵入Customer
聚合根。如果你这样做,命令的责任所在就会变得模糊。如果您从订单导航到客户,会发生什么?Customer
有一个订单列表,但是如果您从OrderRepository
读取订单,它们是否都已填充到内存中?可能不是,但如果您从
CustomerRepository
读取 Customer,则很可能是这样。这里的要点是,对聚合根的分析很重要,一旦定义了聚合,您就必须坚持并尊重它。这导致我偏爱小聚合而不是大聚合,因此在本例中,我将聚合限制为
Order
及其订单行,并且之间没有关联订单
和客户
。Order
和Customer
之间没有正式关联并不意味着我们根本无法将它们关联起来,但我们需要明确的服务来为我们提供给定客户的所有订单。我们不能只是从一个导航到另一个。In general I'm tempted to answer Mu (in the Zen sense), because the scenario is wrong from a DDD perspective. In DDD we start with business requirements and Domain experts and from those we derive a Domain Model.
Although it's a controversial point, the database is almost just an incidental artifact of the business requirements (that almost always state that Entities must be persisted).
That said, I'll try to do my best.
In most cases, an Order is a pretty important business object, and obviously we need to know about the order lines, including the Products in each line, so it would seem like we need the association from order line (Order_Detail) to Product.
However, when given a particular product, we rarely need to know in which orders it was included, so that suggests a one-way relationship right there. We can navigate from order line to product, but not from product to order lines.
However, the above analysis may turn out to be false on a deeper level. Imagine the following conversation between the developer and the domain expert:
Dev: We've created this association from orders to products so that we always know everything about the products in a particular order.
Exp: That sounds good, but what about the supplier?
Dev: That is included as well.
Exp: So what happens when we change the supplier for the product?
Dev: That will be implicitly reflected in the order data as well...
Exp: That's not what we want. We want the data to reflect the order at the time we shipped it.
In this case, it turns out that there's actually an error in the database schema. The problem may be caused by reporting, because business analysists may want to run reports on how different suppliers impact earnings (what do I know).
Such a case may suggest cutting the association from order lines to product altogether. Orders should hold historic (snapshot) product data, not current product data.
We may even entroduce a
ProductSnapshot
Value Object that semantically mirrors aProduct
Entity to model this in the Domain Model.All in all, it seems more and more reasonable to model
Order
as an aggregate of itself and order lines (withProductSnapShots
), but what about the relationship between orders and customers?As I currently understand associations and aggregates, associations define aggregates. Given an order, would we like to know about the customer? Most likely. Given a customer, would you like to know about the orders? Probably.
This suggests a two-way relationship, which makes
Customer
the Aggregate Root. This means that you would have aCustomerRepository
, but noOrderRepository
. Every time you need an Order, you must get it via aCustomer
.In some cases this may make perfect sense, while this could be really clunky in other situations. It really depends on the business requirements...
You might consider creating an
OrderRepository
as well, but that invades theCustomer
Aggregate Root. If you do that, it becomes vague where the responsibility for the Order lies. What happens if you nagigate from Order to Customer?Customer
has a list of Orders, but are they all populated in memory if you read the Order from theOrderRepository
?Probably not, but they are likely to be if you read the Customer from the
CustomerRepository
. The point here is that analysis of Aggregate Roots is important, and once you have defined an Aggregate, you will have to stick with it and respect it.That causes me to favor small aggregates over big aggregates, so in this example, I would constrain the aggregate to
Order
and its order lines, and have no association betweenOrder
andCustomer
.Not having a formal association between
Order
andCustomer
doesn't mean that we can't relate them at all, but we need explicit services to gives us all Orders for a given Customer. We can't just navigate from one to the other.