标签: domain-events

在哪里提出依赖于持久性的域事件 - 服务,存储库或UI?

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

我已经阅读了Udi Dahan的Domain Events以及关于如何做这类事情的许多其他想法,我决定使用一个处理事件消息的基于NServiceBus的主机.我做了一些概念验证测试,这似乎运作良好.

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

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

  • 用户界面提出这些事件是否合适?它知道发生了什么事件,并且只有在成功使服务层保存对象后才能触发它们.让控制器触发域事件似乎有些不对劲.

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

  • 我是否应该完全分离事件,让存储库存储一个Event对象,然后由轮询器服务接收该对象,然后将其转换为NServiceBus的事件(或直接从轮询器服务处理)?

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

  • 更新:我有一个服务层,但让它通过比较过程以确定在保存给定聚合根时应该触发哪些事件似乎很麻烦和过分.由于其中一些事件是粒状的(例如"订单状态已更改"),我认为我必须检索对象的数据库副本,比较属性以创建事件,保存新对象,然后将事件发送到NServiceBus保存操作成功完成.

更新

在我下面发布的答案(下面的方法)之后,我最终做的是在我的域实体中构建一个EventQueue属性List<IDomainEvent>.然后我添加了事件,因为对域的更改是值得的,这使得我可以将逻辑保留在域中,我认为这是合适的,因为我根据实体内部的内容触发事件.

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

asp.net-mvc domain-driven-design nservicebus domain-events asp.net-mvc-3

32
推荐指数
3
解决办法
5724
查看次数

DDD中两个有界上下文之间的通信

我在域中几乎没有不同的有界上下文.CRUD操作的验证建立在每个有界上下文中.

例如,只有创建它的人是组长,我才能创建一个名为GAME的实体.

在这个例子中我有两个有界上下文(BC).一个是游戏BC,另一个是用户BC.为了解决这个问题,在游戏BC中,我必须在继续创建游戏之前向用户BC进行类似IsGroupLeader()的域服务调用.

我不认为DDD推荐这种类型的通信.我也可以在游戏BC中拥有一个用户实体,但我不想这样,因为同一个用户实体在不同的BC中的不同上下文中的使用方式不同.

我的问题是:

  1. 我是否应该使用域事件,其中游戏BC必须向用户BC发送事件以询问用户状态?使用这种方法,我不会像IsGroupLeader那样进行同步调用,而是进行名为is_group_leader的事件.然后游戏BC必须等待用户BC处理事件并返回状态.只有在用户BC处理事件之后,游戏BC才会创建游戏实体.

  2. CQRS是我问题的解决方案吗?

任何想法都赞赏.

domain-driven-design cqrs domain-events bounded-contexts

26
推荐指数
2
解决办法
1万
查看次数

域事件应该在事务内部还是外部引发?

在我们的应用程序中,我们在域模型中发生更改时引发域事件.事件处理程序执行的某些任务必须在引发事件时使用的同一事务中完成,其他任务必须在此事务之外执行.

例如,

将Orderline添加到Order实体时,会引发OrderLineAdded域事件,一个域事件会更改域模型的状态(因此必须在同一事务中执行),然后在事务完成时必须更新UI.

你会如何解决这个问题?

  1. 提升两个事件,一个在事务内,一个在事务外部.
  2. 在事务内部引发事件,但是使用事件处理程序发送Async请求来更新UI?

选项1似乎令人困惑,因为事件名称必须以某种方式传达它们进出事务,但是对于域事件的选项2处理程序必须始终假定它们是在事务内同步调用的.

也许有更好的方法?

architecture domain-driven-design domain-events

19
推荐指数
1
解决办法
3019
查看次数

将Autofac与域事件一起使用

我正在尝试将域事件引入项目中.这个概念在Udi Dahan的帖子中描述 - http://www.udidahan.com/2009/06/14/domain-events-salvation/

这是域事件代码

public interface IDomainEvent { }

public interface IHandleDomainEvents<T> where T : IDomainEvent
{
     void Handle(T args); 
}

public interface IEventDispatcher
{
    void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent;
}

public static class DomainEvents
{
    public static IEventDispatcher Dispatcher { get; set; }

    public static void Raise<TEvent>(TEvent eventToRaise) where TEvent : IDomainEvent
    {
        Dispatcher.Dispatch(eventToRaise);
    }
}
Run Code Online (Sandbox Code Playgroud)

最重要的部分是IEventDispatcher实现,它将域事件与引发事件时发生的任何事件分离开来.诀窍是通过容器连接这个耦合.这是我的尝试

注册所有域事件处理程序的代码....

        var asm = Assembly.GetExecutingAssembly();
        var handlerType = typeof(IHandleDomainEvents<>);

        builder.RegisterAssemblyTypes(asm)
            .Where(t => handlerType.IsAssignableFrom(t)
                        && t.IsClass
                        && !t.IsAbstract)
            .AsClosedTypesOf(handlerType)
            .InstancePerLifetimeScope(); …
Run Code Online (Sandbox Code Playgroud)

autofac domain-events

14
推荐指数
1
解决办法
3324
查看次数

CQRS和域事件

CQRS让我进入思维模式.我正在尝试用CQRS创意开始一个新项目.我喜欢的主要内容是
1)Query和Command的分离.我们的域查询一直是个问题.
2)使用事件存储进行审计 - 我不会将其用于重播 - 至少现在不是.

我很擅长查询方面,我仍然对域事件有一些疑问

如果一个命令导致多个聚合根(例如订单和订单详细信息)的更新,我将把它们限定在UnitofWork(事务性)下.现在,每个域负责在更改发生到其状态时发布事件.

让我们说该命令更改3 orderDetail记录.每个OrderDetail将发布2个事件.最后我们有6个活动.

a)如果我在对域对象进行更改(但未提交事务)后立即发布事件,如何撤消已发布的事件(并且可能已被订阅者使用)

  • 我能想到的是将事件保存在"同一工作单元范围内"的列表中,并且一旦调用了事务提交,就将其存储并发布.这听起来像是人们会做的事吗.

b)如果OrderDetail中的更改要求在Order Aggregate Root中进行某些更改,那么
i)我应该通过处理OrderDetail Aggregate发布的事件来进行这些更改吗?对于前者 让我们说两个订单明细被删除.这使订单状态从"首选"变为"不首选".ii)如果事件错误并且没有更新订单状态怎么办 - 如果订单仍然是首选,那么它将在2天内发货.

添加另一个问题
c)"域事件是所有应用程序状态更改的来源"还是"所有应用程序状态更改的结果"

先感谢您,

三月

cqrs domain-events

13
推荐指数
1
解决办法
3173
查看次数

域事件中的新实体ID

我正在使用CQRS和域事件概念构建具有域模型的应用程序(但没有事件源,只是普通的旧SQL).SomethingChanged类型的事件没有问题.然后我陷入了实施SomethingCreated事件的困境.

当我创建一个映射到具有身份主键的表的实体时,在实体被持久化之前我不知道Id.实体是持久性无知的,所以当从实体内部发布事件时,Id只是不知道 - 它只是在调用context.SaveChanges()之后神奇地设置.那么我如何/何时/何时将Id放入事件数据中?

我在考虑:

  • 包括事件中对实体的引用.这可以在域内工作,但不一定在具有通过事件/消息进行多个自治系统通信的分布式环境中.
  • 重写SaveChanges()以某种方式更新排队以进行发布的事件.但事件是不可改变的,所以这看起来很脏.
  • 删除标识字段并使用实体构造函数中生成的GUID.这可能是最简单的,但可能会影响性能并使其他事情变得更难,例如调试或查询(where id = 'B85E62C3-DC56-40C0-852A-49F759AC68FB',不MIN,MAX等等).这就是我在许多示例应用程序中看到的内容.
  • 混合方法 - 单独保留标识并将其主要用于外键和更快的连接,但使用GUID作为唯一标识符,通过该标识符从应用程序中的存储库中提取实体.

domain-driven-design entity-framework cqrs domain-events

12
推荐指数
1
解决办法
1579
查看次数

延迟域事件的创建和发送

我一直在使用域事件模式 - 它使我们能够在域层中尽可能多地封装行为,并为我们的应用程序的其他部分订阅域事件提供了一种很好的方法.

目前我们正在使用一个静态类,我们的域对象可以调用它来引发事件:

static class DomainEvents
{
    public static IEventDispatcher Dispatcher { get; set; }

    public static void Raise<TEvent>(TEvent e)
    {
        if (e != null)
        {
            Dispatcher.Dispatch(e);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这仅仅是一个IEventDispatcher实际执行调度发布事件的垫片.

我们的调度程序实现只是使用我们的Io​​C容器(StructureMap)来定位指定类型的事件的事件处理程序.

public void Dispatch<TEvent>(TEvent e)
{
    foreach (var handler in container.GetAllInstances<IHandler<TEvent>>())
    {
        handler.Handle(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

这在大多数情况下都可以.但是,这种方法存在一些问题:

仅在成功持久保存实体时才应分派事件

参加以下课程:

public class Order
{
    public string Id { get; private set; }
    public decimal Amount { get; private set; }

    public …
Run Code Online (Sandbox Code Playgroud)

c# messaging domain-driven-design ravendb domain-events

12
推荐指数
1
解决办法
1736
查看次数

在使用带有域事件和ServiceBus的CQRS和DDD时显示View中的更改

我对使用域事件构建读取模型的系统中的流程感到有些困惑.特别是,我们如何处理用户期望数据(及其视图)在完成命令时发生更改的事实,但由于我们的系统架构(对发布事件的非阻塞调用),实际数据库可能不会在页面重新加载?

我希望使用事件和服务总线将我们的一个系统的设计与CQRS更加一致.

让我们说我的流程是这样的:

  1. 用户单击"查看"中的按钮可执行从其帐户中删除付款方式的任务.

  2. 控制器调用PaymentMethodRemovalService,并将其传递给accountId和paymentMethodId.

  3. Controller使用AccountRepository检索Account并调用account.RemovePaymentMethod(id)

  4. 帐户验证可以进行操作并发布事件PaymentMethodRemovedMessage(accountId,paymentMethodId)

  5. 因为事件发布是异步的,所以我们现在必须从服务返回并从控制器返回视图 - 但是我们的实际数据还没有更新!

  6. 处理程序IHandle <PaymentMethodRemovedMessage>,检测事件并从数据库中删除实际行

那么,一个人要做什么?

我可以简单地说,删除显示付款方式的div.这可能适用于AJAX场景,但如果我使用Post-Redirect-Get来支持非JavaScript客户端会怎样.然后我将触发我的Get并从查询端读取数据,可能在更新之前.

我是否只是显示一条通知,说明他们已提交删除付款方式的请求?(这似乎不友好,提交订单是有意义的,但不是,例如,更改地址).

有没有办法将实现更改协调为解耦异步事件并显示反映其当前更改的用户数据?

编辑:我的问题非常类似于CQRS,DDD同步报告数据库 我不得不说,那里给出的答案也提到了这里,有一点气味 - 争论用户界面显示一个带外的更新读取DB,可以这么说.我希望有点清洁.

domain-driven-design nservicebus cqrs event-sourcing domain-events

11
推荐指数
1
解决办法
1586
查看次数

10
推荐指数
2
解决办法
1万
查看次数

域事件处理程序什么时候开始发挥作用?

我有一个简单的两个聚合根和一个常规实体的检查域. Tenant,UserGroup以及User在这个特定样本中的位置TenantUser弥补两个AggregateRoots.

当从UI /服务层接收到命令时,它到达操作只写域的命令处理程序.

您可以说User根本不应该是AggregateRoot,但由于它将被其他人引用,因此它不能是常规实体.(是?)

这两个AggregateRoot需要进行通信.A User不能在不属于a的情况下创建,a UserGroup是有界上下文中的实体Tenant.据推测,我们可以通过构造函数创建一个用户,因为它是一个简单的约束.User.Create(TenantId, UserGroupId)

它生成一个DomainEventwith Date,AggregateVersion和AggregateId(用户).现在我们来到模糊的部分.

打开将此事件提交到商店,此事件将被广播到总线(内存,无论如何).这一点,该点域的事件处理程序,类似命令处理,抓创建的用户,并通知/操纵TenantUserGroup添加UserId

我对解决这个问题的想法是否会走向完全错误的方向?

c# events domain-driven-design cqrs domain-events

10
推荐指数
1
解决办法
2152
查看次数