【问题标题】:DDD principlers and ASP.NET MVC project designDDD 原理和 ASP.NET MVC 项目设计
【发布时间】:2009-08-26 23:32:54
【问题描述】:

两部分问题

我有一个产品聚合;

价格 包装选项 产品描述 产品图片 等等

我已经为一个产品存储库建模,并且没有为任何子类创建单独的存储库。所有数据库操作都通过产品存储库处理。

到目前为止,我是否正确理解了 DDD 概念?有时我会想到一个问题,即拥有一个用于打包选项的存储库可以让我的生活更轻松,方法是使用其 ID 直接从数据库中获取打包选项,而不是要求产品存储库在其 PackagingOptions 集合中找到它并给出它给我..

第二部分是使用 ASP.MVC 框架管理编辑创建操作

我目前正在尝试通过产品控制器管理这些子产品集合的所有添加编辑删除(听起来对吗?)。

我现在面临的一个挑战是:

如果我通过编辑产品的特定包装选项

mydomain/product/editpackagingoption/10

我可以访问包装选项的 ID

但我没有自己的产品 ID,这迫使我编写查询以首先找到具有此特定包装选项的产品,然后编辑该产品和相关包装选项。我可以这样做,因为所有包装选项都有其唯一 ID,但如果我有没有唯一 ID 的集合,这将失败。

感觉很不对劲。。

我想到的下一个选项是在 url 上同时发送产品和包装选项 ID,例如;

mydomain/product/editpackagingoption/3/10

但我也不确定这是否是一个好的设计。

所以我有点困惑。可能对这一切有根本的误解......

如果您能接受这个冗长的问题并帮我整理一下,我将不胜感激。谢谢!

【问题讨论】:

  • 好问题。我无法回答,但在没有产品 ID 的情况下,这有关系吗?如果是一对一的,那么 PackingOption 应该有它自己的 ProductID?
  • 它有一个productid,保存在数据库中。挑战是我如何在没有包装选项存储库的情况下到达那里。

标签: asp.net-mvc design-patterns domain-driven-design


【解决方案1】:

在我看来,这是 DDD 中出现的那些混乱的事情之一。

在代码中,我将聚合根视为它所拥有的任何“关系”以及任何没有聚合根就无法存在的实体对象的容器。

例如,让我们以现在已经被打死的 Customer->Order->LineItem->Product 示例为例。在这种情况下,我展示的聚合根是客户。也就是说,您并不总是希望通过客户获得订单。您可能希望查找特定日期的订单。

反过来,您也不会有没有订单的客户。两者在某种程度上是共生关系,因此一个不是另一个的总根。

关键是您不想通过订单加载客户,但也不一定要通过客户加载订单。

但是,从 Order 开始,您不太可能只想检索 LineItem,而且您肯定不会在没有订单的情况下创建它们。为此,Order 充当了 LineItems 的网关。 LineItem 不需要自己的控制器或存储库。它们仅存在于订单本身中,因此是订单的一部分(在这种情况下,订单成为聚合根)并由订单实体管理。

但是,LineItem 可能与系统内的产品有关系。产品将有自己的控制器、存储库等,因为它们可以存在于聚合根之外。

总结我的漫无边际,我倾向于这样看待它:如果一个实体可以独立存在,它应该有一个控制器。不能单独存在的实体(本例中为 LineItems)只能由其容器(聚合根)管理。

如果/我错了,请一些 DDD 纯粹主义者纠正我吗?

至于您问题的第二部分,我需要更多关于您设想这些其他实体如何工作的详细信息。有了您在此处放置的内容,我想 PackagingOptions 与产品相关,并且将成为产品聚合根的一部分。现在,暗示您正在编辑它们会引出问题,这是系统中的一个查找表,还是它们是一次性值,因此应该被视为值对象?

【讨论】:

  • 感谢您的回答。听起来我们有点像在同一页上,直到我让自己采用一种更激进的方法来汇总所有这些问题的根源。关于你的问题;包装选项我认为应该是价值对象,我会再次研究它。但是我的问题仍然是关于如何为聚合根的实体编辑场景,在该场景中我将子实体的 id 传递给控制器​​,并且由聚合控制器来处理所有其余部分 - 我有点共鸣发送两个聚合 id和这些情况下的孩子 ID,但仍然感觉很乱
  • 我会争辩说,如果一个子实体有一个 id 并且该实体需要被编辑,它保证它自己的控制器。至于获取父关系,如果是 1:1,则将父 ID 与子代一起存储并抓取它,但保存父代以修改具有自己 ID 的子代对我来说有点臭。如果您正在编辑实体本身,那么您不是在编辑聚合,您拥有的东西应该能够独立存在。在我看来,您想通过加载订单来编辑产品,但这是行不通的。此外,值对象没有身份——嗯,通常情况下。
  • 参见 devlicio.us/blogs/casey/archive/2009/02/13/… 作为讨论实体和值对象的起点。
  • 还有一条评论......如果您正在编辑聚合根的子节点而不加载聚合,那么它首先是不正确的。您应该已经加载了聚合根。您不会直接加载 LineItem - 而且您不应该。您将加载订单并允许他们在该订单的上下文中编辑 LineItem。然后您只需“保存”订单,您的子实体就会被保存。您可以添加一些 IsDirty 检查,但父母始终是网关,并且您应该(几乎)永远不要公开孩子的 ID,假设它有一个。
  • 你是对的 - 但我正在处理无状态 http 并且子模块负责编辑实体的子节点,这就是我考虑同时传递聚合根的 id 和我要编辑的实体的 ID,因此我可以加载聚合根然后找到具有该 ID 的子节点并对其进行编辑,然后更新整个聚合根..
【解决方案2】:

凯瓦利亚,

关于您的最后一条评论(无状态 http):

这取决于上下文。在进入细节之前,我应该告诉你一个关于聚合的基本原理:

聚合定义了一组相关对象,出于数据更改的目的,这些对象应被视为一个单元。

这是非常重要的。拥有聚合的目的是强制执行不变量。例如,您可能有“订单不得超过 500 美元”之类的政策。然后,为了执行此策略,您将 Order 和 OrderItem 放在 Order Aggregate 中。这样,任何时候你添加一个新的 OrderItem,它都应该通过 Order 对象添加。在那里,您可以检查总价并确保它不超过 500 美元。如果您的域中没有这样的不变量,那么将所有这些对象一起加载是没有意义的。

现在,回到您的评论:

如果您确实有应该强制执行的不变量,则可以加载整个聚合,即使它可能会有一些开销。是的,HTTP 是无状态的,您加载整个聚合只是为了修改其子对象之一,然后将其丢弃。没关系。这里最重要的是你正在执行你的不变量。这就是 DDD 的用途。

DDD 的目的是捕获您域中的所有业务逻辑。如果您不必加载整个聚合,您肯定可以获得更好的性能,但是您将如何强制执行您的不变量?您很可能必须在存储过程中执行此操作。是的,它可以工作,而且速度很快,但是在维护期间处理存储过程中的业务逻辑是一场噩梦。这就是 DDD 发展的原因。因此,您可以使用面向对象的语言/工具对您的业务需求进行建模,从而使它们更易于理解和修改。

请记住,DDD 是一种很好的方法,但并非适用于所有类型的项目。如果您正在处理一个项目,其中有很多业务逻辑并且由于业务性质而改变它们的机会很高,那么您应该使用 DDD。但是,如果您的项目更多的是“读/写”而没有涉及太多业务逻辑,那么使用 DDD 是一件令人头疼的事情。您可以简单地使用 LINQ to SQL(或 SqlDataAdapters)并将您的对象发送到您的视图。您甚至不必担心查找实体、值对象、聚合、存储库等。

希望对你有帮助,

莫什

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多