12

领域驱动设计鼓励您使用丰富的领域模型。这意味着所有的领域逻辑都位于领域模型中,并且领域模型是至高无上的。持久性成为一个外部问题,因为理想的领域模型本身对持久性一无所知(例如数据库)。

我一直在一个中等规模的单人项目(> 100k 行 Java)中使用它,我发现了许多优点,主要是它提供的灵活性和可重构性,而不是面向数据库的方法。我可以添加和删除域类,点击几个按钮,一个全新的数据库模式和 SQL 层就会推出。

但是,我经常遇到这样的问题,即我发现很难将富域逻辑与支持应用程序的 SQL 数据库这一事实相协调。通常,这会导致典型的“1+N 查询问题”,即您获取 N 个对象,然后对每个再次触发查询的对象执行一个重要的方法。手动优化此过程允许您在恒定数量的 SQL 查询中执行该过程。

在我的设计中,我允许系统插入这些优化版本。我通过将代码移动到一个“查询模块”中来做到这一点,该模块包含数十个特定于域的查询(例如 getActiveUsers),其中两个都在内存中(幼稚且不可扩展)和基于 SQL(用于部署)的实现。这使我可以优化热点,但有两个主要缺点:

  • 我有效地将我的一些域逻辑移动到它并不真正属于的地方,实际上甚至将其推入 SQL 语句中。
  • 这个过程需要我仔细阅读查询日志以找出热点在哪里,然后我必须重构代码,通过将其降低到查询中来降低其级别抽象。

有没有更好、更简洁的方法来协调域驱动设计及其富域模型与您不能将所有实体都保存在内存中并因此仅限于数据库后端的事实?

4

4 回答 4

6

至少有两种方法可以看待这个问题,一种是技术“我可以做些什么来更智能地加载我的数据”版本。我知道的唯一真正聪明的事情是动态集合,这些集合部分加载,其余部分按需加载,可能预加载部分。在JavaZone 2008 上有一个关于这个的有趣的演讲

在我使用 DDD 时,第二种方法更受我关注。如何在不牺牲太多 DDD 优点的情况下制作我的模型,使其更“可加载”。多年来我的假设一直是,许多 DDD 模型对领域概念进行建模,这些领域概念实际上是所有允许的领域状态的总和,跨越所有业务流程以及随着时间的推移在每个业务流程中出现的不同状态。我相信,如果域模型在进程/状态方面稍微标准化一点,那么很多这些加载问题都会大大减少。这通常意味着没有“订单”对象,因为订单通常存在于多个不同的状态,这些状态具有相当不同的语义(ShoppingCartOrder、ShippedOrder、InvoicedOrder、HistoricalOrder)。

但是这里没有灵丹妙药..

于 2008-12-18T17:15:32.983 回答
2

根据我的经验,这是做事的唯一方法。如果您编写的系统试图完全隐藏或抽象持久层,那么您将无法使用持久层的细节来优化事物。

我最近一直在解决这个问题,并且一直在研究一种解决方案,其中持久层可以选择实现代表优化的接口。我一直在玩它,但要使用您的 ListAUsers 示例,它会像这样......

首先编写一个 ListAllUsers 方法,该方法在域级别执行所有操作。有一段时间这会起作用,然后它会开始变得太慢。

当使用富域模型变得缓慢时,创建一个名为“IListActiveUsers”的接口(或者可能更好)。并让您的持久性代码使用适当的技术(可能是优化的 SQL)来实现此接口。

现在您可以编写一个层来检查这些接口并调用特定方法(如果存在)。

这并不完美,我对这类事情没有太多经验。但在我看来,关键是要确保如果您使用的是完全幼稚的持久性方法,那么所有代码​​都应该仍然有效。任何优化都需要作为对此的补充。

于 2008-12-18T17:39:41.030 回答
0

不,不是。反正我并不知道(尽管我很想听听任何 DDD 支持者的相反回应)。

根据我自己的经验,以及与我一起工作的经验丰富的团队的经验,如果您希望从数据库支持的应用程序中获得最佳性能,那么将其架构转变为面向服务的架构是不可避免的。我在这里写了更多关于此的内容(文章讨论了延迟加载的属性,但您可以考虑将这一点应用于需要检索更多数据以完成其工作的类上的任何方法)。

正如您现在所做的那样,您可以从一个丰富的域模型开始,并在出于性能原因需要时将其转换为面向服务的。只要您定义了绩效目标并且您正在实现它们,就没有必要改变一切。我认为这是一种相当不错的务实方法。

于 2008-12-18T16:54:33.633 回答
0

我相信您应该考虑将查询层作为您的域逻辑的一部分。您应该允许自己编写优化的查询,这些查询只能通过对持久性解决方案的“深入”了解来完成。不要试图抽象出一切。此外,批处理是您的应用程序的另一部分,也应该允许您了解您的领域。我发现没有必要仅仅因为我无法将批处理放入我的域模型中而尝试避免批处理。但是,您可以组合这些方法:使用查询来找出需要更改的对象,然后使用您的域逻辑将它们的 id 排队并自行处理每个对象。

于 2015-03-25T11:42:57.990 回答