How to deal with UI data requirements in DDD

cqrsdesign-patternsdomain-driven-designevent-sourcing

I'm using DDD and a CQRS/Event Sourcing architecture so my domain logic is separated in a microservice (e.g: command service) and the read side is in another microservice (e.g: query service).

We often struggle with domain experts when defining requirements. Unfortunately the Product Owner is too aware of the technical implementation and he tends to create requirements for our domain logic without any logic at all just because he "needs" to have some use cases where the end user can view certain information.

My position is that domain driven design is driven by behavior and not by data, therefore we should model in our domain (i.e: events) just the behavior and then include just the data meaningful for that behavior but not the rest of the data if its purpose to be in our domain is just because we want to have a UI that displays that data. My opinion is that if we don't do anything with this data other than displaying it, then we could simply display the same data but queried from a different domain's projection (it's produced by other capability) since they already have the same data.

The real issue here is that due to a business structure change our capability is now quite anemic and our business logic is simply a dispatcher, not a complex domain at all and we are not getting any benefit from event sourcing or CQRS.

The question is: is there any guidelines or literature around this problem that can support my position? Or is it acceptable to have some data pass through our anticorruption layer and reach our domain just because we want to display it in a UI that belongs to us without doing anything at all with it?

PS: As an example we can think of a capability in charge of Product Inventory & Reservations and our capability Bookings and the question would be: should Booking have data and events replicating what already is available in Product Inventory/Reservation capability regarding product details just because we want to see product info in a UI within the context of a booking? Or is it ok to keep our domain agnostic and just query their data in our UI?

Thanks


UPDATE 1: After reading 3 of the answers (thank you!, btw) I agree with some of the points made and I can see some of the value that having duplicated data adds to the read use cases even at the price of having to model this data as part of my domain events, without any logic whatsoever, just to have them ready to be consumed in the query service. However it still feels wrong to me so let me add further context and thoughts and please feel free to update your answers if you change your mind.

Assume my organization has bounded contexts defined. Within a bounded context there are business capabilities which are vertical sections of the business in charge of everything, from pixel to data. Each capability has its own microservice with its own aggregate roots defining the transaction boundaries for each use case. The communication between capabilities is asynchronous (event-based)

Also assume we want to embrace the idea of Micro-UI where each capability is in charge of displaying the info they own.

Does this change your answer? If we had the possibility to have Product Inventory & Reservations displaying the product info in a section of the UI thanks to their micro-UI widget, would you still favor the idea of "polluting" Booking domain (i.e: making booking aggregates aware of product info) just because some business experts say that they want to see product info in the same page where the user is viewing a specific booking but without doing anything with this info in our domain? (e.g: there is no write use case which validation depends on any of these product info fields)

It seems to me your answers, although valid, focus on pragmatism and performance for read use cases, so that's why I ask this πŸ™‚

As a side note I try to embrace Greg Young's idea that every query service composed of projections should be able to be re-built from the event log at any time (i.e: the event sourcing basis) so that a query service should be able to be thrown away and rebuilt by replaying all the events from the beginning of times in an infinite cache fashion.

In order to do that, I would need to have all the product info in the form of our own domain replayable events, which means that my domain has to subscribe to all the events that notify of any change on any product info and include this info in our own domains (making it part of our ubiquitous language). This is fine for many cases, but imagine that Product Inventory & Reservations evolves further and has more and more events that affect their product reservations.

The approach then does not seem that pragmatic to me anymore since we'd have to subscribe to more "external" events in order to keep our cached product info up to date and maybe the business would want every new product info field also in "our" booking UI forcing us to also keep up to date with their new event "versions" and adding more coupling.

My gut feeling is that business does not have their head around this way of working and they keep thinking from the point of view of data requirements rather than behavior requirements. If you were in my position, would you "fight" for moving the responsibility of displaying product info to Product Inventory & Reservations's micro-UI? or that does not change your answer and you still consider it's ok to view UI-data requirements as business-logic?

I know it's a bit of an opinionated thing and that's why I was trying to find some literature around this. If I go to business and tell them to stop polluting our domain with things we don't need that could be achieved by the capability that owns the data, they may not listen to me, but if I go with a Greg Young or Udi Dahan's article to support my view they may think about it! πŸ™‚


UPDATE 2:
I think the debate here summarizes on which approach is better for scenarios where business requirements are not behavior driven but data/view driven:

  1. aggregate info at domain level, the write side (even when there's no logic around this data) by caching other domain info and including it in our ubiquitous language.
  2. aggregate info at query service level, the read side (this is fine as long as the read models are replay-able) so that our read layer can serve the combined info as if it was "ours" (who cares as long as it's there?). The main goal for this layer is to be able to serve info as quick as possible and fully consistent with what has happened on the write side. So no runtime transformation for read operations, info stored in the very same way it's to be consumed.
  3. aggregate info at UI level with a micro-UI approach. Each capability has its own UI widgets and the info is displayed by the capability that owns and understands that info. This is the most technical challenging of all, but the cleanest in my opinion.

In our case the most pragmatic one is the number 2 where the info is merged together (through replayable events' creating projections) so that the info will be on the UI and the business should not care how did we implement this.

Isn't actually the point of CQRS to view the write side (where there is a domain, behaviour rules) differently from the read side and therefore not polluting domain objects with read-requirements?
Then why to suggest including external info in our domain for the solely purpose of satisfying read-requirements? What if there's another way? (i.e: a micro UI approach, or a composing at query side).

I think I'm actually embracing CQRS whilst remaining pragmatic when I say that if a business requirements is data-driven to satisfy a UI, I'd have the UI or query layers worry about this but leave domain and write side out of it as long as I can guarantee that the info will be at UI and the read side is independently scalable, consistent, rebuildable and quick performing to serve info.

Thanks again for your comments. It's good to read other people's point of view.

Best Answer

Your Query/View models are being created for data purpose; They are not part of your domain. You typically use Domain modeling for writes, not reads.

You don’t need DDD to read data. But you do need a well-defined and structured domain model for data validation and modification.

By that logic, you can construct any number of UI-tailored query models to answer queries for frontend screens. It's good practice to store data in Query models in a ready-to-ship format that you can fetch and return to the frontend.

If that means creating duplicated, consolidated structures with denormalized data because the frontend demands it that way, it's perfectly fine. This thought process gives you excellent performance during reads; you don't do any processing.

Related Topic