领域驱动设计和 IoC/依赖注入

2024-03-15

我现在正在尝试应用我学到的有关 DDD 的知识,但我对域模型中的依赖关系流有点困惑。

我的问题是:

  1. 实体是否应该了解域中的工厂、存储库、服务?
  2. 存储库应该了解域中的服务吗?

另一件困扰我的事情是当我想向集合添加实体时如何处理集合。

假设我正在开发一个简单的 CMS。在 CMS 中,我有一个文章实体和标签集合,其中包含标签实体。

现在,如果我想添加带有新标签的关系。更好的方法是什么? (PHP 中的示例)

$article->tags->add(TagEntity);
$articleRepository->save($article);

或者我可以通过服务来做到这一点。

$articleService->addTag($article, TagEntity);

你怎么认为?

Thanks.


实体和值对象永远不应该相互依赖。这些是所有内容中最基本的DDD 的构建块 http://en.wikipedia.org/wiki/Domain-driven_design#Building_blocks_of_DDD。它们代表了您的问题域的概念,因此应该关注问题。通过使它们依赖于工厂、存储库和服务,您会使焦点变得模糊。在实体和值对象中引用服务还存在另一个问题。因为服务也拥有域逻辑,所以您可能会想将域模型的一些职责委托给服务,这最终可能会导致贫血域模型 http://en.wikipedia.org/wiki/Anemic_domain_model.

工厂和存储库只是用于创建和持久化实体的助手。大多数时候,它们在实际问题领域中没有类比,因此根据领域逻辑,从工厂和存储库到服务和实体的引用是没有意义的。

关于您提供的示例,这就是我的实现方式

$article->addTag($tag);
$articleRepository->save($article);

我不会直接访问底层集合,因为我可能想要Article在上执行一些域逻辑(施加约束,验证)Tag在将其添加到集合之前。

避免这种情况

$articleService->addTag($article, $tag);

仅使用服务来执行概念上不属于任何实体的操作。首先,尝试将其适合实体,确保它不适合任何实体。然后才使用服务。这样你就不会得到贫乏的领域模型。

UPDATE 1

引用 Eric Evans 的《领域驱动设计:解决软件核心的复杂性》一书:

应谨慎使用服务,不得剥夺服务的权利 他们所有行为的实体和价值对象。

UPDATE 2

有人否决了这个答案,我不知道为什么。我只能怀疑其中的原因。它可能与实体和服务之间的引用有关,也可能与示例代码有关。好吧,我对示例代码无能为力,因为这是我根据自己的经验得出的意见。但是,我对参考文献部分做了更多研究,这就是我得出的结论。

我并不是唯一一个认为从实体引用服务、存储库和工厂不是一个好主意的人。我在 SO 中发现了类似的问题:

  • 实体可以访问存储库吗? https://stackoverflow.com/questions/827670/is-it-ok-for-entities-to-access-repositories
  • DDD - 领域模型、服务和存储库之间的依赖关系 https://stackoverflow.com/questions/756849/ddd-dependecies-between-domain-model-services-and-repositories
  • DDD - 实体不能直接访问存储库的规则 https://stackoverflow.com/questions/5694241/ddd-the-rule-that-entities-cant-access-repositories-directly
  • 使用服务的 DDD 实体 https://stackoverflow.com/questions/2380882/ddd-entities-making-use-of-services

还有一些关于该主题的好文章,尤其是这篇文章How not在实体中注入服务 http://thinkbeforecoding.com/post/2009/03/04/How-not-to-inject-services-in-entities如果您迫切需要从您的实体调用名为的服务,它还提供了一个解决方案双重派遣 http://en.wikipedia.org/wiki/Double_dispatch。以下是移植到 PHP 的文章中的示例:

interface MailService
{
    public function send($sender, $recipient, $subject, $body);
}

class Message
{
    //...
    public function sendThrough(MailService $mailService)
    {
        $subject = $this->isReply ? 'Re: ' . $this->title : $this->title;
        $mailService->send(
            $this->sender, 
            $this->recipient, 
            $subject, 
            $this->getMessageBody($this->content)
        );
    }
}

因此,正如您所看到的,您没有对MailService服务于您的Message实体,而是将其作为参数传递给实体的方法。本文作者提出了同样的解决方案“DDD:服务 http://devlicio.us/blogs/casey/archive/2009/02/17/ddd-services.aspx" at http://devlicio.us/ http://devlicio.us/在评论部分。

我还尝试了解 Eric Evans 在他的《领域驱动设计:解决软件核心的复杂性》一书中对此的说法。经过简短的搜索,我没有找到确切的答案,但我找到了一个示例,其中实体实际上静态地调用服务,即没有对其的引用。

public class BrokerageAccount {
    String accountNumber;
    String customerSocialSecurityNumber;

    // Omit constructors, etc.

    public Customer getCustomer() {
        String sqlQuery =
            "SELECT * FROM CUSTOMER WHERE" +
            "SS_NUMBER = '" + customerSocialSecurityNumber + "'";
        return QueryService.findSingleCustomerFor(sqlQuery);
    }

    public Set getInvestments() {
        String sqlQuery =
            "SELECT * FROM INVESTMENT WHERE" +
            "BROKERAGE_ACCOUNT = '" + accountNumber + "'";
        return QueryService.findInvestmentsFor(sqlQuery);
    }
}

下面的注释说明如下:

注意:QueryService,一个用于从数据库获取行的实用程序 和创建对象,解释示例很简单,但它是not对于实际项目来说必然是一个好的设计。

如果您查看我上面提到的 DDDSample 项目的源代码,您会发现实体除了对象中的对象外没有任何引用。model包,即实体和值对象。顺便说一句,DDDSample 项目在《领域驱动设计:解决软件核心的复杂性》一书中有详细描述...

另外,我想与您分享的另一件事是关于领域驱动设计雅虎集团 http://tech.groups.yahoo.com/group/domaindrivendesign/message/2300. This message http://tech.groups.yahoo.com/group/domaindrivendesign/message/2305讨论中引用了 Eric Evans 关于引用存储库的模型对象主题的内容。

结论

总而言之,从实体引用服务、存储库和工厂并不好。这是最被接受的意见。尽管存储库和工厂是域层的公民,但它们不是问题域的一部分。有时(例如在维基百科关于 DDD 的文章中)域服务的概念被称为纯制造 http://en.wikipedia.org/wiki/GRASP_(object-oriented_design)#Pure_Fabrication这意味着类(服务)“不代表问题域中的概念”。我宁愿将工厂和存储库称为 Pure Fabrications,因为 Eric Evans 在他的书中确实谈到了有关服务概念的其他内容:

但当手术时实际上是一个重要的领域概念, A 服务构成模型驱动设计的自然组成部分。声明于 将模型作为服务,而不是作为不存在的虚假对象 实际上代表什么,独立操作不会误导 任何人。

根据上述内容,有时从您的实体调用服务可能是明智的选择。然后,您可以使用双重调度方法,这样您就不必在实体类中保留对服务的引用。

当然,总有一些人和作者一样不同意主流观点的人。从实体访问域服务 http://danhaywood.com/2010/04/30/accessing-domain-services-from-entities/文章。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

领域驱动设计和 IoC/依赖注入 的相关文章

  • 使用 MaltParser 和 NLTK 进行依存分析

    考虑一下这个句子 new sent PeterParker loves MaryJane 我尝试使用 malparser 和 NLTK 解析这句话 如下所示 maltParser nltk parse malt MaltParser wor
  • 什么时候使用 IoC 合适?

    我了解 IoC 容器是什么 并且一直在阅读结构图 这项技术似乎很容易使用 我的问题是 使用 IoC 容器的适当粒度级别是多少 我认为 IoC 的应用可能有以下级别 打破所有对象之间的所有依赖关系 当然有点过分了 打破所有主要对象 例如域对象
  • 作曲家和 Yii

    我使用 Composer 作为我的依赖管理器 因为我需要使用 Yii Framework 进行开发 所以我将其添加到我的composer json 文件中 所以它看起来像这样 other properties require other d
  • 存储库本身通常不经过测试?

    抱歉 我对存储库模式 单元测试和 orm 工具还不熟悉 我一直在研究单元测试和存储库模式 并得出一些结论 我想知道我是否正确 存储库模式有助于在使用它的控制器中替换单元测试 例如 对吧 因为创建上下文 在 EF 中 或会话 在 NH 中 的
  • 使用 Unity 跨多种类型注入相同的 DataContext 实例

    假设我有 IRepository 接口及其实现 SqlRepository 它将 LINQ to SQL DataContext 作为参数 假设我有 IService 接口及其实现服务 它需要三个 IRepository IReposito
  • 事件源和 SQL Server 多个关系表

    我们使用 SQL Server 2016 的事件源 我们有完整的客户产品应用程序 每个应用程序都标记为CustomerId并在事件商店中获取单个指南行项目 这是写入事件存储指南的主要标识符 产品应用程序附带许多不同的关系事物 没有引导 但有
  • 类图中的组合和依赖之间的区别?

    我知道 有人就这个案例问过同样的问题 但我仍然不明白 我需要一个具体的答案 谢谢你 D 既然冈努斯没有正确解释构图的含义 我就只好这么做了 正如 Gangnus 所解释的 聚合是一种特殊形式协会具有预期含义部分 整体关系 但没有精确的语义
  • Material UI Icons npm 安装问题:无法解析依赖树

    问题已解决 问题末尾的解决方案 我试图安装材质 UI 图标 https mui com components material icons 在我使用 npm 的 Netflix Clone React js 项目中 但出现以下错误 PS D
  • 查看 osgi 包的依赖关系?

    我正在 Eclipse 中寻找一个可以以图形方式查看所有 osgi 包的依赖项的工具 请问有人有什么想法吗 STAN 捆绑包和项目依赖关系视图是免费的附加组件 这是一个依赖关系图示例 http stan4j com images stori
  • 领域驱动设计:处理原子操作和事务

    必须保证每个聚合内部的一致性 在存储库中执行此操作很容易 因为我始终可以使用数据库或框架中的事务 我对存储库之外发生的事情表示怀疑 一项服务可能需要使用多个聚合来处理请求 在服务处理过程中或在保留聚合时可能会出现问题 如果服务处理过程中出现
  • CQRS - 读取端的事件重播

    我读过几篇关于 CQRS 的博客 它们都解释说 在写入端 事件会持久保存在事件存储中 并且根据请求 事件将被检索并聚合重播 我的问题是为什么读取端不需要聚合事件重播 因为您的读取端不使用聚合 读取端实现为投影 它根据聚合发出的事件流计算当前
  • Gradle 在 Android Studio 中应用插件(导航栏的 safeargs)

    我正在尝试学习 Android studio 并遵循官方教程 但在有关 Gradle 的部分遇到了障碍 因为该指南似乎已经过时了 指南是here https developer android com codelabs build your
  • 寻找 Dagger 辅助注射的示例

    From 匕首讨论 https groups google com forum topic dagger discuss QgnvmZ dH9c 我有一个类 它从对象图中获取一些依赖项 并在运行时从调用者获取其他依赖项 public cla
  • Symfony 4.1 组件 - 依赖注入问题

    我正在用 PHP 重构旧应用程序 我正在尝试使用 Symfony 依赖注入组件将服务注入控制器 或其他服务 但我不知道如何实现这一点 因为 symphony 文档比框架组件更适合使用框架 我已经有了自己的内核 包含所有服务和控制器的容器 控
  • 2 个具有共享 Redis 依赖的 Helm Chart

    目前 我有 2 个 Helm Charts Chart A 和 Chart B Chart A 和 Chart B 对 Redis 实例具有相同的依赖关系 如Chart yaml file dependencies name redis v
  • VS C# 中的依赖地狱,找不到依赖项

    我创建了一个图表 C 库 我们称之为chartlibrary 它本身依赖于多个第三方 dll 文件 在另一个可执行项目中 我们称之为chartuser 我参考了chartlibrary项目 两个项目位于 Visual Studio 中的同一
  • Ruby 依赖注入库

    我一直在研究一些 Ruby 依赖注入库 特别是 我检查了Needle http needle rubyforge org and Copland http copland rubyforge org 它们已经存在很长一段时间了 但用途并不多
  • 如何记录数据库代码以查看数据库对象之间的依赖关系? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想为我的宠物项目编写文档 我的 PostgreSQL 数据库中有 30 个表 近 50 个视图和大约 30 个函数 存储过程 我想看
  • 如何在 Spring 3.1 中构造函数自动装配 HttpServletResponse?

    我有一个请求范围的 bean 并且需要访问 HttpServletResponse 和 HttpServletRequest 对象 我需要在构造函数中访问这些对象 因此属性自动装配不是一个选项 我做了以下事情 Component Scope
  • 创建 AoT 兼容的服务工厂

    我正在尝试为缓存服务创建一个服务工厂 主要要求是每次可以使用不同的字符串实例化单个服务 最终的结果会有多个cache每个服务都由唯一定义databaseName 每个缓存可以有一个或多个stores也由唯一定义storeName 其他服务将

随机推荐

  • 如何从内核读取性能计数器?

    我一直在用户空间使用Linux perf工具 我想编写代码 在每次执行上下文切换时读取线程的性能计数器 所需步骤是 1 获得一种读取性能计数器寄存器的机制 2 每次上下文切换后从调度程序调用步骤 1 我陷入了步骤 1 因为我无法弄清楚要调用
  • 仅在值之间使用分隔符打印

    我的代码输出有一个小问题 并且一直在搜索与此相同的主题 但我没有找到任何主题 while true System out print Enter a positive integer n sc nextInt System out prin
  • WinRT Chrome 应用程序如何在不触发操作系统的“打印”对话框的情况下进行打印?

    WinRT Chrome 应用程序如何在不触发操作系统的 打印 对话框的情况下进行打印 从我在其他地方看到的情况来看 每个 WinRT 应用程序都必须通过 Windows 打印对话框进行打印 但是 当您从 Chrome Metro 应用打印
  • Delphi 10.3 中使用 HTTPRIO 进行基本身份验证

    发生了变化HTTPRIO HTTPWebNode OnBeforePostDelphi 10 3 中的事件 在 Delphi 10 3 之前 事件处理程序是这样定义的 并且运行良好 procedure TForm1 HTTPRIO1HTTP
  • 检查 pandas 中任何列的任何行中是否存在一个值?

    是否有任何函数可以检查 pandas 中任何列的任何行中是否存在某个值 例如 columnA columnB columnC john 3 True mike 1 False bob 0 False 在上面的数据框中 我想知道是否有任何名为
  • Portlet 在 Plone 中如何呈现/排序/布局?

    我正在尝试更改 Plone 站点中 portlet 的全局排序 我搜索了 Plone 源代码 但找不到实际迭代并呈现 portlet 的代码 例如 plone rightcolumn 和 plone leftcolumn 内容提供程序 该代
  • 如何在 Linux 中查找所有以 .rb 结尾的文件?

    我所在的目录包含更多目录 我可以使用什么命令来获取所有以 rb 你可以尝试 find type f name rb
  • 在 Laravel 中将会话从文件传输到数据库

    我已经积累了超过6000万个会话 这导致了文件描述符耗尽的情况 通过将会话传输到数据库解决了该问题 在 Laravel 5 6 上测试 为了这 准备桌子 https laravel com docs 8 x session database
  • 使用子查询中的多个值进行更新

    假设我有一张桌子 items 有一个名为的整数列priority 我正在尝试从另一个表更新它 另一个表是一个临时表 我已经在其中预先计算了所有适当的priority要应用的值 UPDATE items SET priority SELECT
  • 将 DateTimePicker 值设置为 null

    我正在开发一个 WinForms UI 有两个DateTimePicker控制 最初 我想将控件的值设置为 null 直到用户选择日期 如果用户没有选择日期 则会将 null 值传递给数据库 默认情况下 它采用当前日期 您能否提出一些建议或
  • 检测实时应用程序中死锁的方法

    在实时多线程应用程序中检测死锁的方法有哪些 如果我们发现存在死锁 是否有任何方法可以解决它 而无需关闭 重新启动应用程序 有两种流行的方法来检测死锁 一种是让线程设置检查点 例如 如果您有一个具有工作循环的线程 您可以在开始工作时设置一个计
  • Scala 脚本无法在 Ubuntu 上运行

    我有一个以前可以工作的 Scala 脚本 当我尝试在新 PC 上运行它时 编译失败 所以我做了简单的脚本来测试 bin sh exec scala J Xmx2g 0 println test 尝试运行它我得到 test scala err
  • 从谷歌地图中的纬度和经度获取位置地址

    我想在点击谷歌地图并获取纬度和经度后从他们那里获取位置并将其 地址 放入字段中input searchTextField 我该怎么办 我尝试过 但不适合我 DEMO http jsfiddle net DXkZJ http jsfiddle
  • 批量更新 Excel 文件连接字符串

    我们最近更改了 SQL 数据库服务器 我想知道是否有脚本或更简单的方法来更新所有 Excel 文件连接字符串 如果他们使用连接文件会容易得多 但不幸的是它们都是手动设置的 我们有大约 600 份报告 任何帮助深表感谢 Thanks Nick
  • 将多边形转换为网格

    我有很多多边形 理想情况下 所有多边形不得相互重叠 但它们可以彼此相邻 但实际上 我必须考虑到slight多边形重叠 由一定的公差定义 因为所有这些多边形都是从用户手绘输入获得的 这并不像我希望的那样机器精确 我的问题是 是否有任何软件库组
  • MicroMeter:删除/禁用某个标签

    我正在使用默认的 MicroMeter 活页夹 其中之一用于创建仪表jvm memory used 问题是它带有 2 个可用标签 area 和 id 这实际上生成了 6 个我不感兴趣的仪表 我能够做到这一点 Bean public Mete
  • 如何检查 Botframework 上的对话框堆栈

    我正在实现一个反馈对话框 并且想使用IScorable然后关掉 反馈 这个词并推动我的FeedbackDialog到堆栈上 我不希望在收集用户反馈时出现这种行为 有没有办法确定我的FeedbackDialog已经在堆栈上了吗 那么我不会不小
  • Python 中的数据结构

    names Peter John size X M L list price 1 2 3 4 5 6 There are 2 people will buy 3 size of shirt 我想将我的数据结构创建为 name u Peter
  • CakePHP 通过 Auth 记住我

    我已经成功使用了 Auth 但不幸的是 它似乎只适用于 Session 我希望如果用户选中 记住我 复选框 我将使用 Cookie 并且他将登录 2 周 我在官方书籍中找不到任何内容 在谷歌中我只找到了很少而且不是很好的博客文章 有没有办法
  • 领域驱动设计和 IoC/依赖注入

    我现在正在尝试应用我学到的有关 DDD 的知识 但我对域模型中的依赖关系流有点困惑 我的问题是 实体是否应该了解域中的工厂 存储库 服务 存储库应该了解域中的服务吗 另一件困扰我的事情是当我想向集合添加实体时如何处理集合 假设我正在开发一个