Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop RDM vs ADM(再一次)或者中间立场在哪里?_Oop_Domain Driven Design_Domain Model_Anemic Domain Model - Fatal编程技术网

Oop RDM vs ADM(再一次)或者中间立场在哪里?

Oop RDM vs ADM(再一次)或者中间立场在哪里?,oop,domain-driven-design,domain-model,anemic-domain-model,Oop,Domain Driven Design,Domain Model,Anemic Domain Model,在网上和书里花了无数个小时试图对这个问题得出结论,翻阅了许多人的 为了权衡利弊,我决定发布一些关键问题,希望比我聪明的人能够回答: 我阅读了Martin Fowler关于ADM贫血领域模型的文章(他称之为ADM贫血领域模型)和书籍,我了解Eric Evan的DDD领域驱动设计。他们非常受人尊敬, 经验丰富的架构师,他们在将所有这些知识编入他们的书籍和文章中做了非常出色的工作,然而我知道这几乎是不可能的,因为 就像所有印刷媒体一样,它们的例子通常都非常清晰,这可以解释一个概念,但不幸的是,很难将它

在网上和书里花了无数个小时试图对这个问题得出结论,翻阅了许多人的 为了权衡利弊,我决定发布一些关键问题,希望比我聪明的人能够回答:

我阅读了Martin Fowler关于ADM贫血领域模型的文章(他称之为ADM贫血领域模型)和书籍,我了解Eric Evan的DDD领域驱动设计。他们非常受人尊敬, 经验丰富的架构师,他们在将所有这些知识编入他们的书籍和文章中做了非常出色的工作,然而我知道这几乎是不可能的,因为 就像所有印刷媒体一样,它们的例子通常都非常清晰,这可以解释一个概念,但不幸的是,很难将它们应用到实际生活中

在这里,我将快速地解释一些我对您在RDM/ADM+TSService中的观点非常感兴趣的案例,让我们假设一个IoC容器正在进行布线,尽管它基本上是不相关的:

案例I/1: 任务:下订单

行政+技术服务: 要求: 订单项目-数据集+获取类似数据包的数据 OrderService-操作类似于实体对象上的操作

RDM: 要求: 订单项目-数据和功能丰富

案例I/2: 任务:下单并在下单后发送电子邮件

行政+TS 要求: 订单项目-数据集+获取类似数据包的数据 OrderService-操作类似于实体对象上的操作 EmailService-发送电子邮件 可选OrderServiceEmailDecorator-将实际下订单和发送电子邮件的问题分开

评论: 解决: A.使用现有的OrderService并向其中添加电子邮件,在这种情况下,OrderService依赖于EmailService B将问题分离成一个装饰器,我们可以将其与国际奥委会的th服务连接在一起,并根据我们是否需要按需使用

RDM: 要求: 订单项目-数据和功能丰富 ? EmailService-发送电子邮件 ? EventHandler-捕获事件

评论: 在这种情况下,人们通常会推荐以下几点: A.将依赖项注入域层:这将使域层非常沉重,到处都是依赖项。 B随地点传递服务。。。调用:随着越来越多的依赖项进入函数,这将使函数签名一直发生变化。 C提出一个重要操作已经完成的事件:即使是最强大的RDM拥护者也说持久性不应该直接出现在域模型中,这意味着我们是 在此处引发事件,但操作未完全执行。因此,我们可能会在完成之前发送电子邮件。我们可以说,电子邮件可能会失败,因此无论哪种方式都不是完美的, 但我认为实际上下订单是这里的主要操作,发送电子邮件只是一个通知,而且可以重复,再加上屏幕上的通知等等。 你明白这一点了,真的,下订单并不取决于是否能够发送通知电子邮件,但如果坚持订单失败,你显然不想发送电子邮件。 但是有人会说,在存储库中引发这样的事件,在存储库中它会被持久化,这更像是它,但是会传播这些事件

案例I/3: 任务:可以批量下订单,我们只想发送一封包含所有订单的通知电子邮件。请不要开始评论这些订单上的项目, 这是一个例子

行政+TS 要求: 订单项目-数据集+获取类似数据包的数据 OrderService-操作类似于实体对象上的操作 BulkOrderService-将取决于未修饰的OrderService EmailService-发送电子邮件 BulkOrderServiceEmailDecorator-依靠EmailService发送聚合电子邮件

评论: 我们不使用修饰的OrderService,而是使用修饰的BulkOrderService

RDM: 要求: 订单项目-数据和功能丰富 ? EmailService-发送电子邮件 ? EventHandler-捕获事件

评论: 我们的域对象现在变得有点复杂了,我们不能在它上面放置一个.bulkPlace,因为很明显我想在同一个上下文中放置多个订单,所以逻辑必须存在一层上, 假设在控制器级别,调用每个订单上的每个位置,在这种情况下: 从以上BAE继续介绍I/2 RDM解决方案: A.我们如何避开发送到这里的电子邮件?现在我们需要一个.placeWithEmailAndAnyOtherDependency。。还有一个没有。。。? 你不能为了公平而修饰域对象 B现在就在这里传递服务如果你传递null而不是服务,你可能会这样做,它不会发送电子邮件,但这似乎是不可靠的 C引发事件这是一个问题,现在我们将电子邮件发送绑定到此事件,并且我们希望重用.place调用,即使在那里批量订购 将发送多封电子邮件, 除非我们能够以某种方式分离它,否则它也不能真正装饰存储库

现在,这些问题中的一些可能可以通过AOP而不是decorators来解决,但它仍然让人感觉很粗糙

案例I/4: 任务:现在我们有多个入口点,考虑到我们希望能够从调度程序安排周期性批量订单, 但也希望将此功能直接保留在我们的网站上。或者我可以说我们需要一个控制台客户端和一个web客户端,不管怎样,关键是我们的网络控制器不能完成这项工作, 不是直接的

行政+TS 要求不变: 订单项目-数据集+获取类似数据包的数据 OrderService-操作类似于实体对象上的操作 BulkOrderService-将取决于未修饰的OrderService EmailService-发送电子邮件 BulkOrderServiceEmailDecorator-依靠EmailService发送聚合电子邮件

评论: 我们不使用修饰的OrderService,而是使用修饰的BulkOrderService——基本上没有任何变化

RDM: 要求: 订单项目-数据和功能丰富 ? EmailService-发送电子邮件 ? EventHandler-捕获事件

评论: 取决于我们在I/2中做了什么,I/3仍然存在同样的问题,但最重要的是,我们不能再使用我们的控制器在订单中循环,或者如果我们这样做的话 我们回到了一个与ADM+TS类似的分层体系结构

所以,我的主要问题是,我自己在RDM中找不到一个明确、合适的解决方案来解决像这样一个简单的问题,即使在阅读和谷歌搜索之后,人们推荐了不同的东西, 这对解决一件事很好,但会从另一件事中流血,而ADM+TS解决方案在处理这些问题时更灵活。更不用说你不需要DTO-s了, 因为您的ADM本质上是您的DTO,所以您可以将事件传递给视图,所以无需进行任何转换

如果您对如何使用RDM以一种合适的方式逐步处理案例I/2和/3有意见,请留下评论,但是如果您有意见,请提供 并回答所有问题全部4个,或至少最后3个,因为1不是真正的问题!不仅仅是那些你能轻松回答的任务,比如一半的任务/等等

谢谢

更新: 看到一些答案,我可能应该选择一个不同的实体,然后是这个练习的著名顺序,我只想选择一个熟悉的。无论如何,作为补充,试着想象一下,情况I/2,I/3,I/4不是最初的需求,它们是有机进化的。这些要求是逐步增加的。所以,首先你被告知无论何时有订单都要发送一封电子邮件,现在如果你以任何方式将它们耦合在一起,当I/3点击批量订单时,你就会遇到问题。即使你只是把邮件放在一个消息总线上,而且它还没有被发送出去,你怎么批量发送呢?然后你把消息放到总线上,然后删除它/做清理?或者任何其他基于I/2的操作都应该被触发,但基于I/3,不再适用,只需执行它们,然后恢复它们?听起来不对

案例I/2

要解决因响应失败的域事件而过早发送电子邮件的问题,请看一看

案例1/3

如果批量订单的放置是一个用例,那么在域中使其显式。无论您使用的是域模型还是事务脚本,您仍然拥有应用程序服务。反过来,此应用程序服务将有一个实现批量订单放置用例的方法。就域模型而言,您可以潜在地创建一个BulkOrder聚合,现有订单可以选择性地与之关联


总的来说,我认为你关于富域模型的想法太严格了。即使有了丰富的域模型,您仍然可以使用协调的应用程序服务。此外,对域模型进行重构通常可以通过强制隐式概念变得明确,从而深入了解域本身。

订单始终是一个文档,指定客户订购的产品和数量。由于这个原因,它是不可变的,所以我们不能谈论太多的富行为。但重要的是,订单是一个业务概念,它不需要有20个方法。对于价格、总额和税费,订单上附有发票这一业务概念

案例I/1、I/2

我认为使用域事件没有问题。假设订单已经提交

 class ManageOrderHandler:IExecute<CreateOrder>
  {
   public void Execute(CreateOrder cmd)
   {
         Order order=someFactory.CreateOrder(cmd); //not important
         _repository.Save(order);
         _bus.Publish(new OrderSubmitted());
    }
  }
只有在订单被持久化后,才会发布该事件

 class Notifier:ISubscribeTo<OrderSubmitted>
 {
       NotifyService _service;//constructor injected

        public void Handle(OrderSubmitted evnt)
        {
             _service.Notify(/* relevant parameters */);
        }
  }     

  class NotifyService
  {
        public NotifyService(ISendEmails emailNotifier, ISendSms smsNotifier /* etc */)
        {}

        public void Notify(/* arguments */)
        {
             // depending on settings and the arguments send email and/or sms and/or others
        }
  }
当然,NotifyService可以直接作为OrderSubmitted的处理程序,但现在我更喜欢尽可能地解耦

案例I/3、I/4

有点像,但已经调整了。存储库接受多个订单,因此所有订单都将被视为一个事务。将提交包含相关数据的已发布事件。服务 e保持不变

公平地说,我更担心的是,使用消息驱动的体系结构可以让您编写解耦的东西,而且调整或实现新功能更容易。但对于这一点,您确实需要正确地进行领域建模