English 中文(简体)
How to "join" two Aggregate Roots when preparing View Model?
原标题:

Assume that Book and Author are Aggregate Roots in my model.

In read model i have table AuthorsAndBooks which is a list of Authors and Books joined by Book.AuthorId

When BookAdded event is fired i want to receive Author data to create a new AuthorsAndBooks line.

Because Book is an Aggregate Root, information about Author doesn t included in BookAdded event. And i cannot include it because Author root doesn t have getters (according to guidelines of all examples and posts about CQRS and Event Sourcing).

Usually i receive two types of answers on this question:

  1. Enrich your domain event with all data you need in event handlers. But as i said i cannot do it for Aggregates Roots.
  2. Use available data from View Model. I.e. load Author from View Model and use it to build AuthorsAndBooks row.

The last one has some problems with concurrency. Author data can be not available in View Model at the time BookAdded event is handling.

What approach do you use to solve this? Thank you.

最佳回答

As a general advice, let the event handlers be idempotent and make sure you can deal with out of order message handling (either by re-queuing or building in mechanisms to fill in missing data). On the other hand, do question why author and book are such desperate aggregate roots. Maybe you should copy from the author upon adding a book (what the f* is "adding a book", how s that a command). The problem is all these made-up examples. Descend to the real world, I doubt your problem exists.

问题回答

Your question is missing some context, for example what is the user scenario that leads to this event and what is the state you are starting from? If you were writing the BDD tests for this case, what would they look like? Knowing this would help a lot in answering your question.

How you solve the problem of relating an book to an author is domain dependent. First we are assuming it makes sense for your domain to have an aggregate for Author and an aggregate for Book, for example, if I was writing a library system, I doubt I would have an aggregate for authors, since I don t care about an author without his/her book, what I care about is books.

As for the lack of getters, it s worth mentioning that aggregate roots don t have getters because of a preference for a tell-don t-ask style of OOP. However you can tell one AR to do something which then then tells something to another AR if you need. Part of what is important is the AR tells the others about itself rather than writing code where you ask it and then pass it along.

Finally, I have to ask why you don t have the author s ID at the time you are adding the book? How would you even know who the author is then? I would assume you could just do the following (my code assumes you are using a fluent interface for creation of AR, but you can substitute factories, constructors, whatever you use):

CreateNew.Book()
  .ForAuthor(command.AuthorId)
  .WithContent(command.Content);

Now perhaps the scenario is you are adding a book along with a brand new author. I would either handle this as two separate commands (which may make more sense for your domain), or handle the command the following way:

var author = CreateNew.Author()
  .WithName(command.AuthorName);

var book = CreateNew.Book()
  .ForAuthor(author.Id)
  .WithContent(command.Content);

Perhaps the problem is you have no getter on the aggregate root Id, which I don t believe is necessary or common. However, assuming Id encapsulation is important to you, or your BookAdded event needs more information about the author than the Id along can provide, then you could do something like this:

var author = CreateNew.Author()
  .WithName(command.AuthorName);
var book = author.AddBook(command.Content);

// Adds a new book belonging to this Author
public Book AddBook(BookContent content) {
  var book = CreateNew.Book()
    .ForAuthor(this.Id)
    .WithContent(command.Content);
}

Here we are telling the author to add a book, at which point it creates the aggregate root for the book and passes it s Id to the book. Then we can have the event BookAddedForAuthor which will have the id of the author.

The last one has downsides though, it creates a command that must act through multiple aggregate roots. As much as possible I would try to figure out why the first example isn t working for you.

Also, I can t stress enough how the implementation you are looking for is dictated by your specific domain context.

IMHO, populate read model from author/book events, using reordering to handle cases, where events get out of order (view handler is within it s own consistency boundary and should handle ordering/deduplication cases anyway).

The first thing I would ask is why there are concurrency issues in the read model. If the client is sending a reference to the author aggregate inside the AddBook command, where did it get the information from? If the book and author are created at the same time, then your event can probably be enriched. Let me know if I m missing something here.

The last one has some problems with concurrency. Author data can be not available in View Model at the time BookAdded event is handling.

What about "handling the event later"? So you simply put it to the back of the queue until this data is available (maybe with a limit of x tries and x time between each try).





相关问题
How to "join" two Aggregate Roots when preparing View Model?

Assume that Book and Author are Aggregate Roots in my model. In read model i have table AuthorsAndBooks which is a list of Authors and Books joined by Book.AuthorId When BookAdded event is fired i ...

Event Sourcing and dictionary objects

How event sourcing can be combined with several types of dictionary objects like Country, Region, Time Zone etc. and dictionary types from particular domain model like Budget or Availability ...

Event Sourcing and Read Model generation

Assuming Stack Overflow domain problem and the following definition of events: UserRegistered(UserId, Name, Email) UserNameChanged(UserId, Name) QuestionAsked(UserId, QuestionId, Title, Question) ...

Inter-Aggregate Communication in CQRS + DDD + Event Sourcing

How should separate aggregate roots (AR) communicate with one another in an environment built on DDD principles using an event-sourced aggregate back-end? For instance, I have a Facility aggregate ...

热门标签