在哪里引发持久性相关的域事件 - 服务、存储库或 UI?

2023-12-21

我的 ASP.NET MVC3 / NHibernate 应用程序需要触发和处理与我的域对象相关的各种事件。例如,一个Order对象可能有类似的事件OrderStatusChanged or NoteCreatedForOrder。在大多数情况下,这些事件会导致发送电子邮件,因此我不能将它们留在 MVC 应用程序中。

我读过 Udi Dahan 的领域事件 http://www.udidahan.com/2009/06/14/domain-events-salvation/以及关于如何做此类事情的许多其他想法,我决定使用基于 NServiceBus 的主机来处理事件消息。我已经做了一些概念验证测试,这似乎效果很好。

我的问题是哪个应用程序层应该实际引发事件。在成功保留相关对象之前,我不想触发事件(如果持久性失败,则无法发送创建注释的电子邮件)。

另一个问题是,在某些情况下,事件与聚合根下的对象相关联。在上面的例子中,一个Note通过将其添加到来保存Order.Notes收集并保存订单。这带来了一个问题,因为很难评估当某个事件发生时应该触发哪些事件。Order已保存。我想避免在保存更新的副本之前提取对象的当前副本并查找差异。

  • UI 引发这些事件是否合适?它知道发生了什么事件,并且只有在服务层成功保存对象后才能触发它们。让控制器触发域事件似乎有些问题。

  • 成功持久化后,存储库是否应该触发事件?

  • 我应该将事件完全分开,让存储库存储一个Event然后由轮询器服务拾取对象并then变成 NServiceBus 的事件(或直接从轮询器服务处理)?

  • 有一个更好的方法吗?也许让我的域对象对仅在对象持久化后由服务层触发的事件进行排队?

  • Update:我有一个服务层,但让它通过比较过程来确定保存给定聚合根时应触发哪些事件似乎很麻烦且过多。因为其中一些事件是细粒度的(例如“订单状态已更改”),所以我想我必须检索对象的数据库副本,比较属性以创建事件,保存新对象,然后在以下情况下将事件发送到 NServiceBus:保存操作成功完成。

Update

在我在下面发布的答案之后,我最终做了什么(低于 https://stackoverflow.com/a/5912179/240439),是在我的域实体中构建一个EventQueue财产是List<IDomainEvent>。然后,我在域的更改值得时添加了事件,这使我能够将逻辑保留在域内,我认为这是合适的,因为我是根据实体内发生的情况触发事件。

然后,当我将对象保留在服务层中时,我会处理该队列并将事件实际发送到服务总线。最初,我计划使用使用身份 PK 的旧数据库,因此我必须对这些事件进行后处理以填充实体的 ID,但我最终决定切换到Guid.CombPK 这让我可以跳过这一步。


我的解决方案是在域层和服务层中引发事件。

您的域名:

public class Order
{
    public void ChangeStatus(OrderStatus status)
    {
        // change status
        this.Status = status;
        DomainEvent.Raise(new OrderStatusChanged { OrderId = Id, Status = status });
    }

    public void AddNote(string note)
    {
        // add note
        this.Notes.Add(note)
        DomainEvent.Raise(new NoteCreatedForOrder { OrderId = Id, Note = note });
    }
}

您的服务:

public class OrderService
{
    public void SubmitOrder(int orderId, OrderStatus status, string note)
    {
        OrderStatusChanged orderStatusChanged = null;
        NoteCreatedForOrder noteCreatedForOrder = null;

        DomainEvent.Register<OrderStatusChanged>(x => orderStatusChanged = x);
        DomainEvent.Register<NoteCreatedForOrder>(x => noteCreatedForOrder = x);

        using (var uow = UnitOfWork.Start())
        {
            var order = orderRepository.Load(orderId);
            order.ChangeStatus(status);
            order.AddNote(note);
            uow.Commit(); // commit to persist order
        }

        if (orderStatusChanged != null)
        {
            // something like this
            serviceBus.Publish(orderStatusChanged);
        }

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

在哪里引发持久性相关的域事件 - 服务、存储库或 UI? 的相关文章

  • OpenAI Gymnasium,有没有算法支持的库?

    OpenAI 发布了一个名为 Gymnasium 的新库 它应该取代 Gym 库 有许多库都实现了支持健身房环境的强化学习算法 但是 Gymnasium 的界面略有变化 有没有支持 Gymnasium 的算法库 我尝试了 CleanRL K
  • 将 cron 作业配置为在 Jenkins 上每 15 分钟运行一次

    如何在 Jenkins 上每 15 分钟运行一次 cron 作业 这是我尝试过的 在 Jenkins 上 我使用以下 cron 语法设置了每 15 分钟运行一次的作业 14 但该作业每小时执行一次 而不是 15 分钟 我收到有关 cron
  • 为什么没有参数的函数(与实际函数定义相比)可以编译?

    我刚刚看到某人的 C 代码 我很困惑为什么要编译它 有两点我不明白 The 函数原型与实际函数定义相比没有参数 中的参数函数定义没有类型 include
  • Thymeleaf Spring 安全集成 sec:授权不起作用

    我正在使用带有 Spring security 的 Thymeleaf 模板引擎 我还使用 Thymeleaf Spring Security 集成模块来使用 sec authorize 功能 但由于某种原因它不起作用 我没有收到任何错误
  • 在 pip 中为 Flask 应用程序构建 docker 映像失败

    from alpine latest RUN apk add no cache python3 dev pip3 install upgrade pip WORKDIR backend COPY backend RUN pip no cac
  • 类型铸造自我

    以下自我转换对 ClassA 有何作用 这种种姓可以让你进入ClassA吗 A h类 interface ClassA NSObject NSUInteger someNumber ClassB classB property nonato
  • 用于高级搜索/过滤的.Net Web API URL 约定

    我对 Microsoft 的 REST 和 WebAPI 比较陌生 我们正在实现一个中心 REST 服务 它将容纳多种类型的对象获取和设置 作为该项目的领导者 我的任务是提出我们正在使用的正确的 Uri 设计 我想知道关于战争什么想法更好
  • 你遇到过哪些 git 陷阱?

    我遇到的最糟糕的情况是 git 子模块 我在 github 上有一个项目的子模块 该项目无人维护 我想提交补丁 但无法提交 所以我分叉了 现在子模块指向原始库 而我需要它指向 fork 因此 我删除了旧的子模块 并将其替换为同一提交中新项目
  • MsBuild 在 Visual Studio Online 上找不到恢复的 NuGet 包

    我尝试构建一个存储在 Visual Studio Online 上的外部 GIT 存储库中的解决方案 它有以下步骤 1 Git 恢复 有效 2 NuGet 恢复 有效 3 构建 不起作用 查看日志时我的第一个猜测是 MsBuild 没有查找
  • 将文件附加到 PHPMailer

    我目前正在开发一个项目 该项目将文件作为 blob 存储在数据库中 我需要将文件附加到电子邮件并通过 PHPMailer 发送出去 我熟悉 mail gt addAttachment 但是 这个函数似乎只接受文件路径 而我没有 我想知道是否
  • 从哪里开始阅读 SQLite 源代码? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想了解sqlite是如何实现的 并且 想阅读源代码 我已经下载了源代码 我应该开始查看代码的哪一部分 SQLite文档页 http
  • 重构后如何保留 terraform 资源以使用 for_each?

    目前我正在对我们的基础设施进行小型重构 我的项目的当前版本类似于以下内容 我正在尝试使用 for each 来重用变量 resource google cloud scheduler job job name Create All Doss
  • 获取css规则、chrome扩展

    我正在开发 Chrome 扩展程序 它需要访问document styleSheets cssRules 它在某些网站上运行良好 例如w3school 但其他人则不然 比如堆栈溢出 我收到错误 Failed to read the cssR
  • 不确定如何在使用故事板时正确子类化 UIApplication

    我想在 X 次用户不活动 没有触发触摸事件 后返回故事板的初始视图控制器 经过一些研究 我发现检测不活动的最常见方法是触发 NSTimer 并在事件触发时重置间隔 为了检测触发的事件 我们在 UIApplication 的子类中重写 UIA
  • 使用 Javascript 删除字符串的最后一个字符

    我有一个DIV与一些字符 如何在每次单击时删除文本中的最后一个字符DIV itself 删除第一个字符 div on click function this text function index text return text repl
  • 构建 OpenCV 时出错 :: MonitorFromRect 未在此范围内声明

    我试图建立OpenCV version 2 4 8与它一起使用CodeBlocks and MinGw 我按照以下指示进行操作here http kevinhughes ca tutorials opencv install on wind
  • AddressAccessDeniedException :无需 netsh 即可解决它?

    我遇到了异常AddressAccessDeniedException因为我的processus没有注册URL的权限 我首先以管理员身份运行我的程序 好的 它成功了 但我现在想要分发我的应用程序 并且我希望每个用户都能够运行它 而不必成为管理
  • 如何在 iOS 11 上的 Swift 中获取 FLAC 文件元数据?

    我需要获取 FLAC 文件的元数据 我尝试了以下代码 let item AVPlayerItem url URL fileURLWithPath path let commonMetadata item asset commonMetada
  • 每组最大 n 个 SQL 查询的高性能方法

    我正在尝试构建一个基础设施 以便根据需要快速运行回归 从包含我们网络服务器上所有历史活动的数据库中提取 apache 请求 为了通过确保我们仍然回归来自较小客户的请求来提高覆盖范围 我想通过为每个客户检索最多 n 个 为了这个问题 假设 1
  • 将笔记本生成的 HTML 片段转换为 LaTeX 和 PDF

    在我的笔记本里有时会有 from IPython display import display HTML display HTML h3 The s is important h3 question of the day 但当我后来将笔记本

随机推荐