Domain Driven Design鼓励您使用丰富的域模型。这意味着所有域逻辑都位于域模型中,并且域模型是最重要的。持久性成为一个外部问题,因为域模型本身理想上不知道持久性(例如数据库)。
我一直在实践中使用这个中型单人项目(> 100k系列的Java),我发现了很多优点,主要是通过面向数据库的方法提供的灵活性和可重构性。我可以添加和删除域类,点击几个按钮,然后推出一个完整的新数据库模式和SQL层。
但是,我经常遇到一些问题,我发现很难将富域逻辑与支持应用程序的SQL数据库进行协调。通常,这会导致典型的“1 + N查询问题”,其中您获取N个对象,然后对每个再次触发查询的对象执行一个非平凡的方法。手动优化它允许您在恒定数量的SQL查询中执行该过程。
在我的设计中,我允许一个系统插入这些优化版本。我这样做是通过将代码移动到一个“查询模块”,其中包含许多特定于域的查询(例如getActiveUsers),我在其中都有-memory(天真且不可伸缩)和基于SQL(用于部署)的实现。这使我可以优化热点,但有两个主要缺点:
是否有更好,更清晰的方法来协调域驱动设计及其富域模型,因为您不能将所有实体都放在内存中,因此仅限于数据库后端?
答案 0 :(得分:5)
至少有两种方法可以解决这个问题,一种是技术“我能做些什么来加载我的数据更智能”版本。我所知道的唯一非常聪明的东西是动态集合,它们部分加载了按需加载的其余部分,可能会预加载部件。在JavaZone 2008 about this
进行了一次有趣的演讲在我使用DDD的时候,第二种方法更受关注;如何在不牺牲太多DDD优点的情况下使我的模型更加“可加载”。多年来我的假设一直是许多DDD模型模拟域概念,这些概念实际上是所有允许域状态的总和,跨所有业务流程以及每个业务流程中随时间发生的不同状态。我认为,如果域模型在进程/状态方面稍微规范化,那么很多这些加载问题会大大减少。这通常意味着没有“Order”对象,因为ordrer通常存在于附加了相当不同语义的多个不同状态(ShoppingCartOrder,ShippedOrder,InvoicedOrder,HistoricalOrder)。如果你试图封装这是一个Order对象,你总是会遇到很多加载/构造问题。
但这里没有银弹......
答案 1 :(得分:1)
根据我的经验,这是做事的唯一方法。如果您编写的系统试图完全隐藏或抽象持久层,那么您就无法使用持久层的细节来优化事物。
我最近一直在遇到这个问题并且一直致力于解决方案,其中持久层可以选择实现代表优化的接口。我刚刚玩过它,但是使用你的ListAUsers示例就像这样......
首先编写一个ListAllUsers方法,它在域级别中执行所有操作。有一段时间这会起作用,然后开始变得太慢。
当使用富域模型变慢时,创建一个名为“IListActiveUsers”的接口(或者更好的东西)。并且让你的持久性代码使用teten tecniques是适当的(可能是优化的SQL)来实现这个接口。
现在,您可以编写一个检查这些接口的层,并调用特定方法(如果存在)。
这不完美,我对这类事情没有太多经验。但在我看来,关键是确保如果你使用一个完全天真的持久性方法,那么所有代码仍然可以工作。任何优化都需要作为补充。
答案 2 :(得分:0)
不,不是真的。不管怎样我都不知道(虽然我很想听到任何DDD支持者的反应)。
根据我自己的经验,以及与我合作的非常有经验的团队,如果您希望从数据库支持的应用程序获得最佳性能,那么将其体系结构转换为面向服务是不可避免的。我写了more about this here(文章讨论了延迟加载的属性,但您可以考虑应用于需要检索更多数据来完成其工作的类上的任何方法。)
正如您现在所做的那样,您可以从丰富的域模型开始,并在出于性能原因的必要时将其转换为面向服务的模型。只要您已经定义了性能目标并且满足它们,就没有必要转换所有内容。我认为这是一种相当不错的实用方法。
答案 3 :(得分:0)
我相信您应该考虑将查询图层作为域逻辑的一部分。您应该允许自己编写优化的查询,这些查询只能通过持久解决方案的“亲密”知识来完成。不要试图抽象出一切。 此外,批处理是您的应用程序的另一部分,也应该允许您了解您的域。我发现没有必要尝试避免批处理只是因为我无法将其放入我的域模型中。但是,您可以组合这些方法:使用查询来找出需要更改的对象,然后使用域逻辑对其ID进行排队并自行处理每个对象。