计算值和简单阅读-我的域驱动设计困扰人!


9

我一直面临的问题是如何处理由域逻辑驱动的计算值,同时仍然有效地处理数据存储。

例:

我正在通过服务从存储库返回产品列表。此列表受客户端发送的来自请求DTO的分页信息限制。另外,DTO指定一个排序参数(一个客户端友好的枚举)。

在一个简单的场景中,一切工作都很好:该服务将分页和排序表达式发送到存储库,而存储库向数据库发出有效的查询。

但是,当我需要对通过域模型在内存中生成的值进行排序时,一切都将崩溃。例如,Product类具有一个IsExpired()方法,该方法根据业务逻辑返回布尔值。现在我无法在存储库级别进行排序和分页-所有这些操作都必须在内存中完成(效率低下),并且我的服务必须知道何时向存储库发出这些参数以及何时执行排序/分页的复杂性本身。

在我看来,唯一有意义的模式是将实体的状态存储在db中(将IsExpired()设置为只读字段,并在保存之前通过域逻辑对其进行更新)。如果我将此逻辑分为一个单独的“读取模型/ dto”和“报告”存储库,那么我将使模型变得比我想要的更贫乏。

顺便说一句,我看到的每个这样的计算示例实际上都依赖于内存中的处理和掩盖性,因为它从长远来看效率很低。也许我过早地进行了优化,但这并不适合我。

我很想听听其他人如何处理此问题,因为我敢肯定这在涉及DDD的几乎项目中是很常见的。

Answers:


3

我不认为拥有同一数据模型的两个不同域模型会使您的域贫乏。贫血症域模型是一种经常更改的业务逻辑在服务层(或更糟的是在UI层)中被隐藏在域之外的模型。

命令域和查询域模型的分离经常受到支持,并且具有很好的首字母缩写词,您可以在CQRS(命令查询责任隔离)中使用Google进行搜索。

使用领域模型模式,Udi Dahan

过去,我虽然成功地创建了一个处理命令和查询的持久性对象模型,但通常很难对其进行扩展,因为系统的每个部分都将模型拖向另一个方向。

事实证明,开发人员通常会提出比业务实际需要更多的苛刻要求。使用域模型实体向用户显示信息的决定就是这样一个示例。

[...]

对于那些年龄足够大的人,使用COM +的最佳实践指导我们为只读和读写逻辑创建单独的组件。十年后的今天,我们有了诸如实体框架之类的新技术,但这些原则仍然存在。

具有Akka角色和功能域模型的CQRS,Debasish Ghosh

Greg Young在DDD和CQRS上进行了一些精彩的会议。在2008年,他说:“单一模型不能适用于报告,搜索和交易行为”。我们至少有两种模型-一种用于处理命令并将更改提供给另一种用于服务用户查询和报告的模型。应用程序的交易行为通过聚合和存储库的丰富域模型执行,而查询则直接从非规范化数据模型进行服务。

CQRS,马丁·福勒(Martin Fowler)

CQRS引入的更改是将概念模型拆分为单独的模型以进行更新和显示,按照CommandQuerySeparation的词汇分别称为Command和Query。这样做的理由是,对于许多问题,尤其是在更复杂的领域中,具有相同的命令和查询概念模型会导致更复杂的模型,但效果却不佳。

简而言之,让命令模型处理到期并将其传递给数据库的想法绝对没问题。通读上面的第一篇文章,您将看到类似但更复杂的场景。


2

规格

我知道您已经接受了答案,但是,您询问了DDD,与此完全匹配的是Evans所说的“规范”:
直接google图书链接
如果该链接不起作用,请检查这些结果中的图书
如果有这本书,请参阅第226页。

在227页上有3种用于规范的用途:验证,选择,构建新的特殊对象。您的是“选择”-IsExpired。

关于“规范”概念的另一件事是,为了效率起见,它承认您可能需要一个版本的代码对内存中的对象进行操作,而另一个版本的代码可以有效地查询存储库,而无需先获取将所有对象存储到内存中。

在一个简单的世界中,这意味着要在存储库中放入SQL版本,在模型中放入对象版本,这当然有缺点。逻辑在2个地方(不好,有人会忘记更新这些地方),并且您的存储库中有域逻辑。

因此,答案是将两组逻辑都放入规范中。显然,内存版本是存储库版本。例如,如果使用的是n-hibernate,则可以使用其内置查询语言存储库版本。

否则,您将必须为此规范创建一个特殊的存储库方法,该方法可从规范对象中使用。对符合规范的对象的colleciton的调用将通过规范,而不是存储库。至少代码向未来的维护者大喊“我在两个地方,别忘了它”。第231-232页有一个很好的示例,用于解决非常相似的问题。

该规范是DDD的“纯度”的“允许”泄漏/打滑。它仍然可能无法满足您各种用途的需求。例如,ORM可能会生成错误的SQL。可能会有太多额外的编码。因此,您可能必须调用存储库方法,使其几乎就像将SQL放入规范中一样。当然是一件坏事。但是请不要忘记,您的程序必须以合理的速度运行。它不必赢得DDD纯度奖。因此,切换数据存储的现实可能意味着整个程序都会出现老式的说法。也是一件坏事。但不如缓慢的程序(又名“吸盘”)差。显然,如果在各种数据库上都可以立即使用,那么您将为每个规范复制每个数据存储的业务规则。至少您可以掌控此事,并且在交换存储库时可以使用策略模式。但是,如果您正在使用特定的数据库,请记住亚尼

关于CQRS:上面pdr引用Fowler的话在这里仍然适用:“对命令和查询使用相同的概念模型会导致更复杂的模型行不通 ……而且您可能需要使用CQRS或类似方法。但是从开发和维护的角度来看,它要昂贵得多。如果您是与其他竞争的包装供应商,则可能会付款。如果您要为一位客户编写自定义LOB应用程序,那么完美拍摄是一个糟糕的选择。您需要确定是否拥有完全或大部分为双重模型的价值值得您付出额外的努力。规格这是一个很好的折衷方案,因为它允许您在一个需要的程序的一小部分中进行这种分离,并以一种模型的(开发)速度和简便性进行分离。祝好运!


这是很合理的。我想我需要硬着头皮阅读Evans的书:-)我现在看到的是,对这些概念的浅浅理解确实会使您瘫痪!
drogon 2012年

0

我想我会问什么业务逻辑确定isExpired是否为真。数据模型可以由查询执行此逻辑吗?如果是这样,当您以某种方式向其请求产品时,是否可以使存储库足够智能以使用“ isExpired”逻辑?如果没有,也许您确实需要重新检查数据模型。

DDD并不意味着您的存储库必须是愚蠢的-只是意味着您的域需要知道如何与您的存储库对话。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.