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

Are C# events feasible for domain events in DDD?

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

C# 语言内置支持 events using the event keyword together with the EventHandler<> class. Is it feasible to use this instead of a event dispatcher library (such as 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); });
}

如果事件作为根聚合的 public 成员公开,则无论何时从存储库中获取根聚合,应用程序都必须重新订阅根聚合的每个新实例。

每个实例都必须重新订阅,这不是有点不受欢迎吗?该应用程序是否也必须退订?

除非事件声明为 static,但我听说过有关静态事件和内存泄漏的坏消息。这会是一个问题吗?

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

如果事件是在存储库中声明的(可能是 Entity Framework 核心 DbContext),那么当使用 .AddDbContext 向 ASP.NET 核心依赖项处理程序注册时方法它将在“Scoped”生命周期中注册(每个客户端请求一次),因此除非事件被声明为静态,否则应用程序将必须重新订阅存储库的每个新实例,这将发生在每个新传入的 HTTP 请求。

在采用域驱动设计的应用程序中将 C# 事件用于域事件是否可行,或者它只是一个不可行的不幸想法?

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

如果我们查看 Mediatr,它会订阅一个事件源并创建新的命令处理器来处理传入的域事件。关键是它创建了新的命令处理器,因此它能够调用它们的方法。此外,域事件和命令处理器之间存在 one-to-one 对应关系,系统启动时除了 Mediatr 本身外不需要任何其他东西。

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

那么当你开始扩展时会发生什么? Mediatr 的扩展性很好,因为它为每个领域事件创建了一个命令处理器。您的提议不会扩展,因为要处理 2 个相同的事件类型,您需要手动创建 2 个命令处理器,并且每个命令处理器都会收到两个传入的域事件,因为它们都订阅了相同的 C# 事件。

可以通过编码解决所有这些混乱问题,但这正是 Jimmy Bogard 已经完成的工作。不要重写所有刚刚启动 NuGet 的东西,而是拉下 Mediatr,然后用你节省的所有时间去和你的孩子一起玩。

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

如果应用程序是 Web 应用程序,那么它的 DbContext 范围限定为 HTTP 请求的生命周期,并且所有聚合的生命周期也很短,持续到 HTTP 请求的持续时间.因此,注册 C# 事件处理程序很麻烦,而且因为您必须在获取 DbContext 或聚合之后执行此操作,它必须在控制器内部重复、无处不在。因此,对于将 C# 用于事件的 Web 应用程序来说,这是一个糟糕的选择,最好将其委托给单例 class,就像 MediatR 所做的那样。

如果应用程序维护在应用程序的生命周期内存在的持久性 DbContext 和在应用程序的生命周期内存在的根聚合,那么您可以使用 C# 事件并只注册一次事件处理程序.这样的应用程序可以是命令行应用程序、后台服务或带有 UI.

的桌面应用程序