在域事件中包含用户 ID

Including user id in domain events

我一直在从事一个使用 DDD 架构的新文档管理项目。我是 DDD 和事件驱动设计的新手,所以这是一次学习经历。
我的申请结构如下:

域拥有我所有的域逻辑,基础设施是持久性,应用程序主要是命令和处理程序,而 webapi 就是 webapi。

现在我正在努力实现用户授权,目前我已经决定使用授权处理程序,它会在执行命令或查询之前进行权限检查。我认为这给了我很好的灵活性来执行复杂的基于资源的授权,因为我的许多权限将取决于某个实体的当前状态。

目前为止一切顺利,我已经在我的应用程序层中实现了授权,将大部分用户特定信息留在了我的域模型之外。

现在,我想弄清楚的问题是如何最好地将用户信息包含在我的域事件中,从我的域 类。

例如,我有一个聚合体,比方说它的文档,文档有一个特定的审批流程。因此,当文档获得批准时,我想提出一个领域事件,例如

public class DocumentApproved : IDomainEvent
{
    public DocumentApproved (Package package)
    {
        DocumentId= document.Id;
        Timestamp = DateTime.Now;
    }
    public Guid Id { get; }
    public Guid UserId { get; }
    public DateTime Timestamp { get; }
    public Guid PackageId { get; }
}

系统需要可审计,所以我计划将域事件用作审计跟踪以及引起副作用或向其他系统发送消息的机制。

Document 实体可能具有如下所示的方法:

 public void Approve()
    {
        State = State.Approve(this);
        ApprovalRevision.Next();
        Events.Add(new DocumentApproved(this));
    }

我的问题

我想将 UserId 包含在几乎每个适用的域事件中,但我想不出如何做到这一点,而不必将 userId 作为我域实体上每个相关方法的参数,这意味着它们只是为了创建域事件而作为参数,因为所有授权类型 activity 都将在应用程序层中发生。

这是解决此问题的合理方法,还是我应该尝试以不同的方式捕获用户 activity,例如我的请求管道,并将它们与域事件分开?这听起来不太对我。让 userId 成为可能导致域事件的每个方法的参数确实不是问题,但这似乎只会给每个域实体签名增加混乱。

为了引发我的领域事件,我正在做类似于建议的事情 here,其中实体有一个 DomainEvents 集合,并且在保存实体之前通过 MediatR 发布它们。

 I think this gives me good flexibility perform complex resource-based authorization, since many of my permissions will depend on the current state of a certain entity.

听起来授权是业务规则的重要组成部分,应该在领域层实现。您需要使用用户信息丰富域事件这一事实表明用户应该是域的一部分。

在不确切知道域的情况下,我可以想象你有一个不变量,例如:"A document can only be approved by the line manager of the author"。没有 users/roles 的概念,你不能在域中断言这个不变量。

最实用的方法可能是将用户信息(连同其他 session/request 信息)简单地存储为事件元数据(希望您的事件存储支持)并在域外处理这个问题.

例如,在一个应用程序中,我编写了一个可以配置 EventMetadataProvider 的事件存储实现,它在应用程序的组合根中注册并使用当前请求上下文附加元数据,例如用户ID、远程IP地址等

请注意,您仍然可以使用 approver/creator/etc 丰富一些领域事件。对事件消费者来说最重要的地方,但理想情况下不是通用的 UserId 概念。