如果我正在处理操作/事务类型的应用程序,我发现DDD很自然。但是,我总是以合理的方式处理报告类型的函数。
我正在讨论的报告不一定是报告生成,也是执行相对复杂查询的函数。 (比如,给出交易者所做的所有订单的摘要,或显示具有特定股票的交易账户的账户摘要等)。它们可以是一些查询或支持功能,与这些操作功能一起使用。
对于这样的函数,如果我们可以在SQL(或任何查询语言)中执行连接,获取我们感兴趣的列,并返回按摩结果集,这是很自然的。但是,使用DDD似乎不太顺利:我们需要一个额外的特殊存储库或者现有最相关的存储库返回一个特殊的“实体/值对象”(这是专门的结果集)。这些特殊的“实体”实际上并没有任何域名含义。
如果我们想要使用有意义的域层,那么可能会从不同的存储库中创建大量额外的查找,以及域或服务层中的大量聚合工作,这很容易导致可怕的性能下降。
我还想过为这类函数设置另一个“路径”,它不会通过“DDD路径”,有自己的方式从DB获取报告数据,组成显示结果。但是它会使应用程序变得不必要地复杂化,更糟糕的是,我们提供了一个额外的路径,以便更习惯于传统的面向数据库开发的开发人员可能倾向于使用这条路径,即使它不合适。
我认为这种情况很常见(通常一个大系统不会包含操作,还有报告和查询功能),我想知道人们如何处理它?</ p>
答案 0 :(得分:19)
在DDD报告方面,在大多数情况下是一个单独的有界上下文和一个支持子域,其中域驱动设计将是过度的。记住最重要的DDD概念:将您的建模工作集中在核心域上,并使用最简单的解决方案实现其他所有功能。
答案 1 :(得分:2)
我们最近开始使用DDD进行系统开发。我和你的问题一样,但最终还是选择了Command Query Responsibility Segregation(CQRS)[Fowler,Young,Dahan]。虽然需要“数据库路径”进行查询,但我并不觉得直接使用DB for Commands(那些改变域状态)变得很诱人。分离非常明确 - 命令通过域,查询直接到DB。
答案 2 :(得分:1)
一种方法是建立一个单独的报告系统,该系统从您应用的数据存储中运行数据馈送,以更多关系格式存储另一个数据副本。
我使用的一个快捷方式是创建一个视图或存储过程,将连接的数据返回到一个简单的哑对象中。
答案 3 :(得分:0)
相对复杂的查询。 (比如,给出交易者所做的所有订单的摘要,或显示具有特定股票的交易账户的账户摘要等)
存储库通常执行此类任务。我认为你担心如何有效地实现这一点,答案就是“延迟加载”。
例如,让我们采取“交易者所做的所有订单的摘要”。 “摘要”是一个报告任务,所以让我们把它放在一边。域任务是“查找交易者的所有订单”。您可能有这样的存储库方法:
List<Order> findOrdersByTrader(Trader trader);
您可以通过仅加载每个订单的最小(摘要)信息来实现此目的。如果您随后将存储库接口注入Order
实体,则实体本身可以在需要时调用存储库以加载其他子实体。
更新:您的评论会让问题更加清晰 - 我之前误解了汇总部分。似乎“订单摘要”确实属于您的域名。有时候,概念是域的一部分并不明显,但如果它是用户谈论的功能(“我想查看此交易者下达的订单摘要”)并且没有现成的对象可以执行此操作,这是您域中隐藏概念的标志。毕竟,您需要某些对象来跟踪“每个股票的订单数量”,并且没有理由此对象不能成为您域名的一部分。