gpt4 book ai didi

domain-driven-design - 将现实世界的逻辑放入 DDD 领域层时遇到问题

转载 作者:行者123 更新时间:2023-12-03 12:51:42 25 4
gpt4 key购买 nike

尽管研究过Domain Driven Design很长一段时间,现在仍然有一些我只是弄清楚的基础知识。

似乎每次尝试设计一个丰富的domain layer ,我还需要很多Domain Services或厚Application Layer ,我最终得到了一堆近乎贫乏的领域实体,除了“GetTotalAmount”等之外,其中没有真正的逻辑。关键问题是实体不知道外部的东西,将任何东西注入(inject)实体是不好的做法。

让我举几个例子:

1. 用户注册服务。用户被持久化在数据库中,生成并保存文件(用户帐户需要),并发送确认电子邮件。

带有确认电子邮件的示例已在其他线程中进行了大量讨论,但没有真正的结论。有人建议将逻辑放在 application service 中。得到 EmailServiceFileServiceinfrastructure layer 注入(inject).但是那样我就会有域之外的业务逻辑,对吧?其他人建议创建 domain service得到 infrastructure services注入(inject) - 但在这种情况下,我需要拥有 infrastructure services 的接口(interface)里面domain layer ( IEmailServiceIFileService )看起来也不太好(因为 domain layer 不能引用 infrastructure layer )。其他人建议实现 Udi Dahan's Domain Events然后让 EmailService 和 FileService 订阅这些事件。但这似乎是一个非常松散的实现——如果服务失败会发生什么?请在这里告诉我您认为正确的解决方案。

2. 从数字音乐商店购买歌曲。购物车已清空。继续购买。调用支付服务。将发送一封电子邮件确认。

好的,这可能与第一个示例有关。这里的问题是,谁负责编排这笔交易?当然,我可以将所有东西都放在带有注入(inject)服务的 MVC Controller 中。但如果我想要真正的 DDD,所有业务逻辑都应该在域中。但是哪个实体应该有“购​​买”方法? Song.Purchase() ? Order.Purchase() ? OrderProcessor.Purchase() (域服务)? ShoppingCartService.Purchase() (应用服务?)

在这种情况下,我认为很难在域实​​体中使用真实的业务逻辑。如果向实体中注入(inject)任何东西不是一个好习惯,那么除了检查它自己的(及其聚合的)状态之外,他们怎么能做其他事情呢?

我希望这些示例足够清楚,可以显示我正在处理的问题。

最佳答案

Dimitry 的回答指出了一些值得寻找的好东西。您经常/很容易发现自己处于您的场景中,数据通过不同的层从 db 向上推到 GUI。

Jimmy Nilsson 的简单建议“值(value)对象、值(value)对象和更多值(value)对象”启发了我。人们通常倾向于将注意力集中在名词上并将它们建模为实体。自然地,您经常难以找到 DDD 行为。动词更容易与行为联系起来。一件好事是让这些动词作为值对象出现在您的域中。

我在尝试开发域时为自己使用的一些指导(必须说构建一个丰富的域需要时间,通常是几次重构迭代......):

  • 最小化属性(获取/设置)
  • 尽可能多地使用值对象
  • 尽可能少地暴露。使您的域聚合方法直观。

  • 不要忘记您的域可以通过验证变得丰富。只有您的域知道如何进行购买以及需要什么。

    当您的实体从一种状态转换为另一种状态(工作流验证)时,您的域还应该负责验证。

    我会给你一些例子:
    这是我在博客上写的一篇关于您关于贫血域 http://magnusbackeus.wordpress.com/2011/05/31/preventing-anemic-domain-model-where-is-my-model-behaviour/ 的问题的文章

    我也真的可以推荐 Jimmy Bogard 的关于实体验证和使用 Validator 模式以及扩展方法的博客文章。它使您可以自由地验证基础设施,而不会弄脏您的域:
    http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/

    我使用 Udi 的领域事件取得了巨大成功。如果您认为您的服务可能会失败,您也可以使它们异步。您还可以将其包装在事务中(使用 NServiceBus 框架)。

    在你的第一个例子中(现在只是头脑 Storm ,让我们的头脑更多地思考值(value)对象)。
  • 您的 MusicService.AddSubscriber(User newUser)应用程序服务从演示者/ Controller /WCF 获得一个新用户的调用。
    服务已经获得IUserRepositoryIMusicServiceRepository注入(inject)ctor。
  • 音乐服务“Spotify”通过 IMusicServiceRepository
  • 加载
  • 实体 musicService.SignUp(MusicServiceSubscriber newSubsriber)方法接受一个值对象 MusicServiceSubscriber .
    此 Value 对象必须在 ctor 中采用 User 和其他强制对象
    (值对象是不可变的)。在这里您还可以放置逻辑/行为,例如处理订阅 ID 等。
  • SignUp 方法的作用是触发域事件 NewSubscriberAddedToMusicService .
    它被 EventHandler HandleNewSubscriberAddedToMusicServiceEvent 捕获了得到IFileServiceIEmailService注入(inject)它的ctor。此处理程序的实现位于应用程序服务层,但事件由域和 MusicService.SignUp 控制.这意味着域处于控制之中。事件处理程序创建文件并发送电子邮件。

  • 您可以通过事件处理程序持久化用户或使 MusicService.AddSubscriber(...)对此的方法。两者都将通过 IUserRepository 执行此操作但这是一个品味问题,也许它会如何反射(reflect)实际领域。

    最后......我希望你能掌握上面的一些东西......无论如何。最重要的是开始向实体添加“动词”方法并进行协作。您还可以在域中拥有未持久化的对象,它们仅用于在多个域实体之间进行调解,并且可以托管算法等。

    关于domain-driven-design - 将现实世界的逻辑放入 DDD 领域层时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7306109/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com