DDD 基础设施服务
Posted
技术标签:
【中文标题】DDD 基础设施服务【英文标题】:DDD Infrastructure services 【发布时间】:2010-11-25 22:06:43 【问题描述】:我正在学习 DDD,我在基础设施层有点迷失。
据我了解,“所有好的 DDD 应用程序”都应该有 4 层:表示层、应用程序、域和基础架构。应使用存储库访问数据库。存储库接口应该在域层和存储库实现中 - 在基础架构中(参考 DDD: Where to keep domain Interfaces, the Infrastructure?)。
应用程序、域和基础设施层应该/可能有服务(参考 Services in Domain-Driven Design),例如,基础设施层中的 EmailService 发送电子邮件消息。
但是,在基础设施层内,我们有存储库实现,用于访问数据库。那么,在这种情况下,存储库是数据库服务吗?基础设施服务和存储库有什么区别?
提前致谢!
【问题讨论】:
【参考方案1】:坚持 DDD 定义,存储库与服务不同。存储库直接与实体相关,通常是聚合根。服务定义了实际上不属于您域中单个实体的行为。您绝对可以在每一层中找到服务,尽管它们解决的问题类型因层而异,并且可能与 DDD 的概念服务不同。
在概念级别工作时,DDD 存储库与 DDD 服务的不同之处在于它专门与实体持久性相关联。服务可以解决您可能遇到的任何域、应用程序或基础架构问题。
您到处都会遇到与 DDD 的术语冲突。例如,DDD 存储库与 Martin Fowler 的 PoEAA 书中的Repository pattern 不同,尽管它可能采用这种模式。这通常是许多人困惑的根源。
如果您始终将领域模型置于您所做的一切的中心,这对 DDD 会有所帮助。说到分层 DDD 应用,我经常选择Jeffrey Palermo's Onion Architecture。看看这个。下载CodeCampServer,一个使用此架构的示例应用程序。我认为它非常适合 DDD 编程。
祝你好运!
【讨论】:
【参考方案2】:也许这有助于了解潜在的项目结构。
可能的组装或封装结构:
项目.域 项目.基础设施.数据 项目.基础设施.组件 项目.基础设施.服务
可能的命名空间或文件夹结构:
项目.域 -n- 模块 ----n- 帐户 --------f- Account.xx --------f- AccountRepository.xx --------f- Contact.xx -n- 营销 --------f- RegionRepository.xx -n- 共享 -n- 服务
Project.Infrastructure.Data (OR-Mappers) -n- 表 -n- 浏览量 -n- 程序 -n- 函数
Project.Infrastructure.Components(通用) -n- 邮件 -n- 密码学 -n- 用户界面
Project.Infrastructure.Services(特殊操作) -f- DoingSomethingService1.xx -f- DoingSomethingService2.xx -f- DoingSomethingService3.xx
域实体和值类型不使用域服务。应用层使用域的服务。 Domain Repository 对象使用 Infrastructure.Data 对象返回 Domain 对象。
【讨论】:
DDD 的重点不是要有一定的项目结构,而是要对你的实体有行为。当您说“复制您的 ORM 对象”时,听起来您正在使用贫血域,这根本不是真正的 DDD。行为(域状态突变)应该在域对象内部,而不是在服务中。 当然,瑞恩。 DDD 与某个项目类型无关。这是一个实现细节。这就是为什么我说查看项目结构可能会有所帮助。通过“制作副本”,我说的是属性,而不是行为。在大多数情况下,域对象将类似于数据库对象。那是什么意思?这意味着属性将是相似的,除了 Aggregate 和 Composition 关联。由于这使您感到困惑,因此我将删除该文本。如果它让你感到困惑,我相信它会让其他人感到困惑。 我认为您的意思是属性,而不是属性。我不是要批评,只是澄清一下。我认为域对象上的属性是一种反模式。最近我一直在走向准 cqrs 路线:只读我的域上的属性和改变状态的方法。我的视图大约有 50% 来自非规范化数据库视图,50% 来自域模型。我选择的方法只是取决于数据的复杂性。 @Roy 既然您说“域实体和值类型不使用域服务”,这是否意味着它也不使用存储库? Aggregates 怎么样,它也不使用 Service 或 Repository 吗? 过去,我有实体或聚合根 (AR) 调用存储库。现在,我的 AR 由存储库保存或检索。域可能是一件非常有趣的事情。您必须对它的设计和实现感到满意。有些 AR 很简单,但有些可能具有包含数百万个项目的集合属性。从实体或 AR 调用存储库通常太诱人了,因为有时它看起来很自然。我建议尝试通过不从实体或 AR 调用存储库来了解您能走多远。您正在从事哪种类型的项目或解决方案?【参考方案3】:关于 DDD 的不幸之处在于“服务”一词。它应该是“域服务”。将域视为实体和值对象,而服务是处理动作、操作和活动的一种方式。
至于存储库,它们只是一个外观,应该像您的域的集合一样。如果您正在使用 ORM 或编写自己的 ORM,那么您的所有域对象都应该通过这种方式来实现持久性,而不是直接使用这些服务。
【讨论】:
好吧,也许你误解了我的问题,或者我误解了答案。在基础设施层中,如果我们有一个处理邮件 API 的服务,我们将其称为“电子邮件服务”,但从数据库中检索数据的代码称为“存储库实现”。不就是同一种“基础设施服务”吗?【参考方案4】:为什么要将存储库实现放入 Infrastructure 中?它们根本与“基础设施”无关。
根据Evan's Blue Book,存储库是域模型的一部分。因此,我将存储库接口放在域模型中。存储库的实现有时也是如此。
基础设施应包含“基础设施”代码,它可以是一项服务,使您能够通过 smtp 轻松发送电子邮件,或者为您抽象数据库访问的代码(因此,您可以在存储库中使用一些类,但它不是您的存储库)。
因此,不要将您的存储库放入“基础设施”,因为它们不属于那里。 对我来说,可以在基础设施中找到的类是可以在其他不同项目中使用的类,明白我的意思吗?与您的域模型或应用程序不紧密相关的类属于基础架构。存储库实现与特定应用程序紧密耦合。 :)
【讨论】:
我认为存储库是特定于域的,不一定是特定于应用程序的。以这种方式来看,在域模型层中定义存储库接口是有意义的。我将数据访问视为特定于域的基础架构问题,与应用程序代码分开。我明白您所说的可重用库,但我将这些库分成不同的模块:一个(或多个)可重用模块,包含诸如 EmailSender 之类的类,以及一个(或多个)特定于域的基础架构模块,包含诸如客户存储库。不同的模块,相同的层。 确实,存储库实现不应该在应用程序中。它应该在域模型中。但是,应用层应该控制存储库使用的事务范围。 我认为传统存储库应该在域模型之外实现,如果它包含进行数据访问的实际基础设施。但是根据 Jimmy Nilsson 的 NWorkspace 概念,我认为存储库可以将非基础设施实现移回更有意义的领域层。 存储库接口可以进入 CORE/DOMAIN,存储库的实现在基础设施中很好,尤其是在任何 ORM 的情况下。我们不希望 CORE 中存在 ORM 依赖项。 因此,如果您的存储库接口位于“DOMAIN”中,并且您的实现位于基础架构中,这意味着您的基础架构了解您的域(您依赖于“INFRA”中的“DOMAIN”) .我不认为你想要那个......以上是关于DDD 基础设施服务的主要内容,如果未能解决你的问题,请参考以下文章