Model 扩展富域模型

Model 扩展富域模型,model,scalability,domain-driven-design,Model,Scalability,Domain Driven Design,领域驱动设计鼓励您使用丰富的领域模型。这意味着所有的域逻辑都位于域模型中,并且域模型是最高的。持久性成为一个外部问题,因为域模型本身在理想情况下对持久性一无所知(例如数据库) 我在一个中型单人项目(>10万行Java)中实际使用了它,我发现了许多优点,主要是它比面向数据库的方法提供的灵活性和可重构性。我可以添加和删除域类,点击几个按钮,一个全新的数据库模式和SQL层就会出现 然而,我经常遇到这样的问题:我发现很难将富域逻辑与应用程序背后有SQL数据库这一事实相协调。通常,这会导致典型的“1+N查

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

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

然而,我经常遇到这样的问题:我发现很难将富域逻辑与应用程序背后有SQL数据库这一事实相协调。通常,这会导致典型的“1+N查询问题”,即获取N个对象,然后对每个对象执行一个非平凡的方法,再次触发查询。通过手动优化,您可以在固定数量的SQL查询中完成此过程

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

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

有没有更好、更清晰的方法来协调域驱动设计及其丰富的域模型,使其与您不能将所有实体都存储在内存中,因此只能局限于数据库后端这一事实相协调?

没有,不太可能。无论如何,我并不知道(尽管我有兴趣听到DDD的支持者对相反的回答)

根据我自己的经验,以及与我一起工作的经验丰富的团队的经验,如果您希望数据库支持的应用程序获得最佳性能,那么将其体系结构转换为面向服务是不可避免的。我写道(文章谈到了懒惰加载的属性,但是您可以考虑这个点应用于类中需要检索更多数据来完成它的工作的任何方法)。
正如您现在所做的那样,您可以从一个丰富的域模型开始,并在出于性能原因的需要时将其转换为面向服务的。只要您已经定义了绩效目标并正在实现这些目标,就没有必要改变一切。我认为这是一个相当不错的实用方法。

至少有两种方法可以解决这个问题,一种是技术上的“我可以做什么才能更智能地加载数据”版本。我所知道的唯一真正聪明的事情是动态集合,其中一部分是按需加载的,其余部分是按需加载的,并可能预加载部分。会议上有一次有趣的谈话

第二种方法在我与DDD合作的过程中一直是我关注的焦点;我如何使我的模型更“可加载”,而不牺牲太多的DDD优点。多年来,我的假设一直是,许多DDD模型对域概念进行建模,这些域概念实际上是所有业务流程中所有允许的域状态的总和,以及随着时间的推移每个业务流程中出现的不同状态。我相信,如果域模型在进程/状态方面稍微规范化一些,那么这些加载问题就会大大减少。这通常意味着没有“Order”对象,因为Order通常存在于多个不同的状态中,并且附加了相当不同的语义(ShoppingCartOrder、ShippedOrder、InvoicedOrder、HistoricalOrder)。如果您试图将其封装为单个订单对象,那么最终必然会遇到许多加载/构造问题


但是这里没有银弹。

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

我最近遇到了这个问题,并且一直在研究一个解决方案,其中持久层可以选择实现代表优化的接口。我只是在玩它,但以你的ListAUsers为例,它是这样的

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

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

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


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

我认为您应该考虑将查询层作为域逻辑的一部分。您应该允许自己编写优化的查询,这些查询只能在“熟悉”持久性解决方案的情况下完成。不要试图把一切都抽象掉。 此外,批处理是应用程序的另一部分,也应该允许它了解您的域。我发现没有必要尝试避免批处理,因为我无法将其放入我的域模型中。你