领域驱动设计和 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/依赖注入 的相关文章

  • 以编程方式注入依赖项asp.net core

    我刚刚开始Asp net core 依赖注入 我的概念可能不准确 这篇 docs asp net 帖子 https docs asp net en latest mvc controllers dependency injection ht
  • .Net 6 控制台应用程序:WebApplication.CreateBuilder 与 Host.CreateDefaultBuilder

    我正在研究 NET 6 并希望构建一个简单的控制台应用程序 并进行一些依赖项注入 据我所知 为了使启动 现在只是程序 文件更具可读性 已经做了很多工作 让我有点困惑的是 所有改进似乎都是针对 API 项目中使用的 WebApplicatio
  • 从 EntityManagerFactory 连接 EntityManager 的问题

    我知道 spring 有它自己的 JPA 对象工厂 但我想让它按原样工作 从那里我将研究最佳实践 我只是处于理解过程中 这是我尝试为其生成 bean 的实际代码 EntityManagerFactory emf Persistence cr
  • 通用接口依赖注入工厂

    我正在玩 DI 和 Ninject 我愿意征求有关如何以其他方式解决此问题的建议 我试图确保 ICreateOrders 的具体实现可以被替换 如果需要的话 也许还有另一种方式或模式 像这样的解决方案 C Ninject IoC 和工厂模式
  • 我可以使用 ivy.xml 文件中的属性来避免依赖项的重复版本号吗?

    这是我的 ivy xml 的一部分现在的样子
  • Maven编译错误:包不存在

    我正在尝试向现有企业项目添加 Maven 支持 这是一个多模块项目 前 2 个模块编译和打包没有问题 但我面临编译错误 我尝试在多个模块中使用相同的依赖项 我的结构是 gt parent gt pom xml gt module 1 gt
  • 关于存储库的领域驱动设计问题

    我正在尝试实现 DDD 因此我创建了以下类 用户 域模型 UserRepository 管理对象的中央工厂 UserMapper UserDbTable 映射应用程序功能并提供 CRUD 实现的映射器 我的第一个问题是 当模型需要与持久层通
  • LocationAwareLogger 上的 SLF4J NoSuchMethodError

    这是一个以前曾被问过的问题 但不幸的是 似乎没有解决方案对我有用 我面临这个异常 带有删节的堆栈跟踪 java lang NoSuchMethodError org slf4j spi LocationAwareLogger log Lor
  • 我们什么时候需要在 Angular2 的服务中使用 @Injectable ?

    All 我对 Angular2 还很陌生 当我到达依赖注入部分时 Injectable 符号让我有点困惑 只是想确保我对 Injectable 的理解是正确的 Injectable 表示它后面的类可以作为服务注入 Injectable 表示
  • 注入包含接口的所有已注册实现的 Enumerable

    给出以下接口 public interface IMyProcessor void Process 我希望能够注册多个实现 并让我的 DI 容器将它们的可枚举注入到这样的类中 public class MyProcessorLibrary
  • 在DDD中,值对象的实际优势是什么?

    到目前为止 我知道实体对象有 ID 而值对象没有 但在最常见的示例中 人员实体附加了地址值对象 创建单独的地址对象而不是仅将地址属性保留在 Person 实体中的最大优点是什么 除了已经提到的事情之外 格雷格 杨 http weblogs
  • IDependencyResolver 是反模式吗?

    我正在对旧版 ASP NET 应用程序进行一些架构更改 我模拟了 ASP NET MVC 的 IDependencyResolver 为依赖解析创建了一些类的原型 我不会发布 因为它的界面几乎相同 但采用其他自然语言 我发现它可能被认为是服
  • 在 Android 中使用 lambdaj

    有人尝试过在android开发中使用lambdaj库吗 当我创建一个简单的小型java应用程序时 它对我来说工作得很好 但我无法在android应用程序中使用它 UPDATE 我正在添加 lambdaj lambdaj 2 3 2 with
  • @DependsOn 注释的逆

    Spring 可能还有其他 DI 容器 但我使用的是 Spring 识别 DependsOn 注释 您可以使用它来标识必须在该 bean 之前启动的任何其他 bean 例如 Component DependsOn initiatedFirs
  • 何时使用接口,何时使用高阶函数?

    给定一个具有以下层的 ASP NET MVC 应用程序 UI 视图 CSS Javascript 等 控制器 服务 包含业务逻辑和数据访问 没有单独的数据访问层的原因是我正在使用 SQL 类型提供程序 以下代码可能不起作用 因为它只是原始草
  • PHP 中的依赖注入

    我一直在研究依赖注入 我是在关注某件事还是完全没有关注 代码是好是坏 依赖注入与否 下面的代码是CMS系统的基础 现在有一个名为 page details 的表 其中存储了所有网页 目录 文件结构 htaccess index php cl
  • 在 ASP.NET Core 的中间件中注入服务

    我想根据 HTTP 标头值注入服务 所以我有 2 个类 DbDataProvider 和 InMemDataProvider 它们都是从 IDataProvider 实现的 每当进行 API 调用时 客户端都会传递一个标头 该标头确定是需要
  • 在ConfigureServices中注入依赖

    在我的 ASP Net Core 应用程序中 我需要在以下位置注入一些依赖项 在我的例子中是一个存储库 ConfigureServices method 问题是该方法不允许使用多个参数来注入依赖项 该怎么办呢 这是我的代码 public v
  • .net 4.7 中的依赖注入?

    我对 DI 有哪些集成选项有点困惑 我发现它对于 net core 对于我的特定项目 来说非常简单 但我不需要构建跨平台应用程序 也看不到使用 core 的优势 但是 net 框架应用程序似乎仍然使用 Global asax 设置且没有 S
  • Dropwizard 和 Guice:注入环境

    我目前正在构建一个基于 Dropwizard Guice Jersey 的应用程序 其中数据库访问暂时由 JDBI 处理 我想要实现的是拥有典型的企业架构 其中资源访问服务类访问 DAO 类 而 DAO 类又访问数据库 如果能以正确的 DI

随机推荐

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

    我一直在用户空间使用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 的知识 但我对域模型中的依赖关系流有点困惑 我的问题是 实体是否应该了解域中的工厂 存储库 服务 存储库应该了解域中的服务吗 另一件困扰我的事情是当我想向集合添加实体时如何处理集合 假设我正在开发一个