0
votes

First of all as for "simplified DDD/CQS pattern" I am referencing https://github.com/dotnet-architecture/eShopOnContainers example dotnet application (which is not "strict" DDD/CQRS). However I am not dotnet developer and my question is related to general design patterns / code organization.

My own case is as follows: I have "conference" domain with "conference" root aggregate and "conference_edition" aggregate. In RDBMS language conference has many editions. In this domain all "communication" with outside world is done through root aggregate "conference". So there are use cases like Conference.create(), Conference.addDraftEdition(), Conference.publishEdition() etc.

I have also "booking" domain which manages bookings for specific conference editions (people book tickets for a conference edition).

My question is related to the read model / aggregate of "conference" service (domain?). CMS of this application needs following read model for a conference (example in json for simplicity):

{
  "name": "My conference name",
  "editions": [
    {
      // name, startDate, endDate can be directly read from "conference" domain aggregate
      "name": "My conference 2021",
      "startDate": "...",
      "endDate": "...",
      // following data come from "booking" domain
      "completedBookingsCount": 20,
      "pendingBookingsCount": 10,
      "canceledBookingsCount": 2
    },
    ...
  ]
}

My question is how to "make" this read model (as in simplified DDD example eShopOnContainers I assume that queries are allowed to directly query domain aggregates):

  1. I could make additional (additional to domain aggregates) read aggregate and update it by handling BookingCreated, BookingCompleted and BookingCanceled integration events from "booking" service to keep statistics per conference edition in "conference" service read model and then just read it along with "conference" and "edition" aggregates. In such case how I should organize my code regarding additional read aggregate? I mean it's still aggregate so it should "have" it's own domain with commands like "increaseCompletedEventsForConferenceEdition" etc? Or I should make it as simple as possible without all that domain constraints like for example:
class BookingCanceledIntegrationEventHandler {
  handle (BookingCanceledIntegrationEvent event) {
    editionStatisticsRepository.increaseCanceledBookingsCount(event.editionId);
    if (event.prevBookingStatus == 'pending') {
      editionStatisticsRepository.decreasePendingBookingsCount(event.editionId);
    } else if (event.prevBookingStatus == 'completed') {
      editionStatisticsRepository.decreaseCompletedBookingsCount(event.editionId);
    }
  }
 }
  1. Or maybe I should handle this read model "shaping" on api gateway side? I mean to query two services "conference" and "booking" and join data on api gateway side to compose required read model?

  2. Or maybe I should add edition statistics as kind of ValueObject to "edition" aggregate? However AFAIK ValueObjects must be immutable and moreover it feels wrong to put these data (edition statistics) to "conference" domain...

How it should/could be organised according to DDD/CQS pattern?

1

1 Answers

1
votes

Either of the first or second is reasonable. The choice between them will be heavily influenced by how much you want to spread business logic into infrastructure.

Since the conference and booking domains appear to be different bounded contexts, the third option is really only justifiable if there's some change to a conference edition that's only allowable if bookings are in a certain state.