C#事件对于DDD中的域事件是否可行



我读到人们在域驱动的设计中使用事件调度库来处理域事件。

C#语言内置了对使用event关键字和EventHandler<>类的事件的支持。使用它而不是事件调度程序库(如MediatR(是否可行?

我知道域事件通常是在持久化时调度的,而不是在调用聚合上的方法时。但是,通过将事件添加到List<Action>中,可以推迟事件的引发。

事件声明:

public event EventHandler<InvoiceCreatedEventArgs> InvoiceCreated;

延迟事件引发:

private ICollection<Action> _events = new List<Action>();
public void AddDomainEvent(Action action)
{
_events.Add(action);
}
protected virtual void OnInvoiceCreated(InvoiceCreatedEventArgs e)
{
AddDomainEvent(() => { InvoiceCreated?.Invoke(this, e); });
}

如果事件公开为根聚合的公共成员,那么每当从存储库中提取根聚合时,应用程序都必须重新订阅根聚合的每个新实例。

每次都要重新订阅,这不是有点不可取吗?申请也必须取消订阅吗?

除非事件被声明为static,但我听说过关于静态事件和内存泄漏的坏消息。这会引起关注吗?

如果使用C#事件,它们是属于根聚合还是属于存储库?

如果事件是在存储库(可以是实体框架核心DbContext(中声明的,那么当使用.AddDbContext方法向ASP.NET核心依赖性处理程序注册时,它将在"Scoped"生存期内注册(每个客户端请求一次(,因此,除非事件被声明为静态,否则应用程序将不得不重新订阅存储库的每个新实例,这将在每个新的传入HTTP请求时发生。

在采用领域驱动设计的应用程序中,将C#事件用于领域事件是否可行,或者这只是一个不可行的、注定要失败的想法?

如果不区分C#事件和域事件,很难遵循您的叙述。然而,如果我正确理解你的提议,你设想的是一个C#组件,它侦听实体事件流,然后通过C#事件将这些传入的域事件发布给应用程序中的侦听器。如果这就是你的提议,那么你必须努力工作才能实现,但效果并不好。

如果我们看一下Mediator,它订阅了一个事件源,并创建了新的命令处理器来处理传入的域事件。关键是它创建了新的命令处理器,以便能够在它们上调用方法。此外,域事件和命令处理器之间存在一对一的对应关系,在系统启动时不需要任何东西,只需要Mediator本身。

使用C#事件,命令处理器由某个东西创建,然后注册自己以接收特定类型的C#事件。命令处理器启动链接,而不是事件源订阅者。为了处理所有类型的事件,在启动时,您必须至少创建每种类型的命令处理器中的一个,并让它将自己注册为C#消息的接收器,该消息将域事件作为有效负载。

那么当你开始按比例缩放时会发生什么?Mediator的伸缩性很好,因为它为每个域事件创建了一个命令处理器。您的方案无法扩展,因为要处理2个相同事件类型的命令处理器,您需要手动创建2个命令处理器,而这些命令处理器中的每一个都将接收两个传入的域事件,因为它们都订阅了同一个C#事件。

可以对所有这些混乱进行编码,但吉米·博加德已经做到了。与其重写所有这些,不如启动NuGet,放下Mediator,然后用你节省的所有时间和你的孩子一起玩。

这取决于应用程序的类型。

如果应用程序是一个web应用程序,那么它的DbContext范围为HTTP请求的生存时间,并且所有聚合的生存时间也很短,持续到HTTP请求的持续时间。因此,注册C#事件处理程序是很麻烦的,因为在获取DbContext或聚合之后必须这样做,它必须在控制器内,重复地,无处不在。因此,对于使用C#处理事件的web应用程序来说,这是一个糟糕的选择,而且最好像使用MediatR那样将其委托给一个singleton类。

如果应用程序维护一个在应用程序生存期内有效的持久DbContext和一个在该应用程序生存期间有效的根聚合,那么您可以使用C#事件,只需注册一次事件处理程序。这样的应用程序可以是命令行应用程序、后台服务或具有UI的桌面应用程序。

最新更新