事件溯源聚合根是否应该有权访问事件溯源存储库?

发布于 2024-09-03 02:28:41 字数 2047 浏览 1 评论 0原文

我正在开发 事件源 CQRS 实现,在应用程序/域层使用 DDD 。我有一个如下所示的对象模型:

public class Person : AggregateRootBase
{
    private Guid? _bookingId;

    public Person(Identification identification)
    {
        Apply(new PersonCreatedEvent(identification));
    }

    public Booking CreateBooking() {
        // Enforce Person invariants
        var booking = new Booking();
        Apply(new PersonBookedEvent(booking.Id));
        return booking;
    }

    public void Release() {
        // Enforce Person invariants
        // Should we load the booking here from the aggregate repository?
        // We need to ensure that booking is released as well.
        var booking = BookingRepository.Load(_bookingId);
        booking.Release();
        Apply(new PersonReleasedEvent(_bookingId));
    }

    [EventHandler]
    public void Handle(PersonBookedEvent @event) { _bookingId = @event.BookingId; }

    [EventHandler]
    public void Handle(PersonReleasedEvent @event) { _bookingId = null; }
}

public class Booking : AggregateRootBase
{
    private DateTime _bookingDate;
    private DateTime? _releaseDate;

    public Booking()
    {
        //Enforce invariants
        Apply(new BookingCreatedEvent());
    }

    public void Release() 
    {
        //Enforce invariants
        Apply(new BookingReleasedEvent());
    }

    [EventHandler]
    public void Handle(BookingCreatedEvent @event) { _bookingDate = SystemTime.Now(); }
    [EventHandler]
    public void Handle(BookingReleasedEvent @event) { _releaseDate = SystemTime.Now(); }
    // Some other business activities unrelated to a person
}

到目前为止,根据我对 DDD 的理解,Person 和 Booking 都是单独的聚合根,原因有两个:

  1. 有时业务组件会从数据库中单独提取 Booking 对象。 (即,已被释放的人员由于信息不正确而修改了之前的预订)。
  2. 每当需要更新 Booking 时,Person 和 Booking 之间不应出现锁定争用。

另一项业务要求是,一个人一次只能进行一次预订。因此,我担心在读取端查询查询数据库,因为那里可能存在一些不一致(由于使用 CQRS 并具有最终一致的读取数据库)。

是否应该允许聚合根通过对象的 id 查询事件源后备存储(根据需要延迟加载它们)?还有其他更有意义的实施途径吗?

I'm working on an event-sourced CQRS implementation, using DDD in the application / domain layer. I have an object model that looks like this:

public class Person : AggregateRootBase
{
    private Guid? _bookingId;

    public Person(Identification identification)
    {
        Apply(new PersonCreatedEvent(identification));
    }

    public Booking CreateBooking() {
        // Enforce Person invariants
        var booking = new Booking();
        Apply(new PersonBookedEvent(booking.Id));
        return booking;
    }

    public void Release() {
        // Enforce Person invariants
        // Should we load the booking here from the aggregate repository?
        // We need to ensure that booking is released as well.
        var booking = BookingRepository.Load(_bookingId);
        booking.Release();
        Apply(new PersonReleasedEvent(_bookingId));
    }

    [EventHandler]
    public void Handle(PersonBookedEvent @event) { _bookingId = @event.BookingId; }

    [EventHandler]
    public void Handle(PersonReleasedEvent @event) { _bookingId = null; }
}

public class Booking : AggregateRootBase
{
    private DateTime _bookingDate;
    private DateTime? _releaseDate;

    public Booking()
    {
        //Enforce invariants
        Apply(new BookingCreatedEvent());
    }

    public void Release() 
    {
        //Enforce invariants
        Apply(new BookingReleasedEvent());
    }

    [EventHandler]
    public void Handle(BookingCreatedEvent @event) { _bookingDate = SystemTime.Now(); }
    [EventHandler]
    public void Handle(BookingReleasedEvent @event) { _releaseDate = SystemTime.Now(); }
    // Some other business activities unrelated to a person
}

With my understanding of DDD so far, both Person and Booking are seperate aggregate roots for two reasons:

  1. There are times when business components will pull Booking objects separately from the database. (ie, a person that has been released has a previous booking modified due to incorrect information).
  2. There should not be locking contention between Person and Booking whenever a Booking needs to be updated.

One other business requirement is that a Booking can never occur for a Person more than once at a time. Due to this, I'm concerned about querying the query database on the read side as there could potentially be some inconsistency there (due to using CQRS and having an eventually consistent read database).

Should the aggregate roots be allowed to query the event-sourced backing store by id for objects (lazy-loading them as needed)? Are there any other avenues of implementation that would make more sense?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

陪我终i 2024-09-10 02:28:41

首先,在您的案例中,您真的需要事件溯源吗?对我来说看起来很简单。事件溯源既有优点也有缺点。虽然它为您提供了免费的审计跟踪并使您的域模型更具表现力,但它使解决方案变得复杂。

好的,我假设此时您已经考虑了自己的决定,并且决定继续使用事件溯源。我认为您缺少消息传递作为聚合之间通信手段的概念。 Pat Helland 的论文(顺便说一句,它不是关于 DDD 或事件溯源,但涉及可扩展性)。

这个想法是聚合可以互相发送消息来强制某些行为。聚合之间不能有同步(也称为方法调用)交互,因为这会引入一致性问题。

在您的示例中,人员 AR 会向预订 AR 发送预订消息。该消息将以某种异步且可靠的方式传输。 Booking AR 将处理此消息,如果已被其他人预订,它将回复 ReservationRejected 消息。否则,它将发送 ReservationConfirmed。这些消息必须由 Person AR 处理。也许,他们会生成另一个事件,该事件将被转换为发送给客户的电子邮件或类似的内容。

无需在模型中获取查询数据。只是发消息。如果您想要示例,可以下载 Ncqrs 项目的“Messaging”分支的源代码并查看在场景测试课上。它使用 Blue Book 中的 Cargo 和 HandlingEvent 示例演示了 AR 之间的消息传递。

这能回答你的问题吗?

First of all, do you really really need event sourcing in you case? It looks pretty simple to me. Event sourcing has both advantages and disadvantages. While it gives you a free audit trail and makes your domain model more expressive, it complicates the solution.

OK, I assume that at this point you thought over your decision and you are determined to stay with event sourcing. I think that you are missing the concept of messaging as a means of communication between aggregates. It is described best in Pat Helland's paper (which is, BTW, not about DDD or Event Sourcing, but about scalability).

The idea is aggregates can send messages to each other to force some behavior. There can be no synchronous (a.k.a method call) interaction between aggregates because this would introduce consistency problems.

In your example, a Person AR would send a Reserve message to a Booking AR. This message would be transported in some asynchronous and reliable way. Booking AR would handle this message and if it is already book by another person, it would reply with ReservationRejected message. Otherwise, it would send ReservationConfirmed. These messages would have to be handled by Person AR. Probably, they would generate yet another event which would be transformed into e-mail sent to customer or something like that.

There is no need of fetching query data in the model. Just messaging. If you want an example, you can download sources of "Messaging" branch of Ncqrs project and take a look at ScenarioTest class. It demonstrates messaging between ARs using Cargo and HandlingEvent sample from the Blue Book.

Does this answer your question?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文