3

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.

5

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.

  • I see your point. Please read my Update1 in case it changes your answer, because it seems to me that your answer follows a valid pragmatic approach that could change if the context was different (like a Micro-UI context). – iberodev Sep 10 at 13:02
  • This answer is still spot on even after your edits. If they want to display non-updatable product info because it is useful to the user, then you should in order to have a rich useful interface. This reminds one of why we bother with CQRS in the first place instead of simply using domain models for display. There often is not a one-to-one correspondence between domain and UI, nor should you impose one. Your queries are free to efficiently join whatever is relevant to the user without needing to load a potentially heavy product info domain object with many irrelevant fields. – Mike Sep 10 at 14:45
  • Mike's response covers my thought process. I would only add one more thing: a business need is real. No matter how the requirement affects your code or dev process or data structure, it needs to be satisfied. It's unrealistic and impossible (even naive, if I dare say) to assume that we can shape requirements to maintain code purity. It is indeed sad that your Product Owner is technology-aware; you will miss out on some innovative opportunities. But still, the code needs to be bend over backward to satisfy business needs, when necessary. – Subhash Bhushan Sep 10 at 15:51
  • I agree with Mike, and that's precisely why I believe my domain objects should not know anything about data that does not care about because there's no logic around it, even if the read models care about it. I am actually embracing CQRS along with DDD's focus on behavior. However as you point out, if business wants to view certain info, maybe there's a better way (see Update2) which is moving this info composing outside the domain layer towards the read side either at the query service (exposed with a remote facade) or directly at UI level (if following a MicroUI approach for example). – iberodev Sep 12 at 14:06
  • Yep, we are talking about the same thing. And yes, your query service is data-oriented and does not have to be part of your domain model. – Subhash Bhushan Sep 12 at 14:28
0

Not everything is black or white. Even implementing DDD, CQRS or MS there should be room for flexibility. I mean, you should never follow blindly what DDD, CQRS, MS or other disciplines theory say. You should make these disciplines work for you and not otherwise.

That said,

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?

DDD, CQRS and MS don't forbid information sharing among domains (boundaries). Sharing information and duplicity is expected most of the times. What matters is keeping the responsibility for its maintenance (data consistency) in the proper domain (boundary). There's nothing wrong with Booking asking for information to Product. There's nothing wrong with storing temporally, as read-only (cache), such information in the Booking domain. From the CQRS' Query-side, performance can be a good reason for this. @Subhash made a good point about this.

Since you are struggling with CQRS and Microservices, the main goal should not be preventing domains sharing information, it should be in how to allow it with the least coupling possible.

Bear also in mind that, from the Booking standpoint, a Product can be interpreted as something different than it's at its source. From the Booking's domain point of view, a product could be a mere tuple id-description. You can use the perspectives to reduce the amount of information that is usually shared.

0

You are completely right, that DDD should be about modeling behavior, but I do consider presenting an object to the user as a valid behavior for an object to have.

I am aware that in most projects and also in most architecture patterns the UI and all external interfaces are considered second rate citizens, but it doesn't have to be that way. The requirements, business people even end users speak about the UI, use language like the "Balance Chart", the "Profile Picture". Those are all things which have to exists in the application, it is part of the "ubiquitous language".

Also, if something doesn't have any behavior, except to be a row in a table on the UI, then I would call it Row, even if we know it's a Customer. In other words as you've said, behavior defines the model, if the behavior of a Customer is not different from a Product (i.e. both are just presented as data rows or something), then they are the same object. You don't have to "name" them just because the backend system distinguishes them. You only have to model the differently if they have different behavior.

So, if your object needs to be presented with some additional information then it is a valid use-case to include that additional information for the purposes of presenting it correctly. This is of course if other solutions, like including a html view directly from another system, or splitting services differently, etc., are not feasible.

  • Disagree on this answer. UI and external interfaces are usually pushed outside a model not because they are not important. But they are finicky, change often, have multiple representations and mean different things to different personas. Coupling them with the model takes the focus away from the model's core behavior, forcing you to dive deeper into implementation details. – Subhash Bhushan Sep 10 at 18:31
  • 1
    A "Balance Chart" is indeed part of Ubiquitous language and requirements are derived from it. But it could be a bar chart or a pie chart or something entirely different for people on mobile. This aspect should not be part of a domain's behavior, because the intent of a user consuming a Balance Chart to make decisions does not change with the representation. – Subhash Bhushan Sep 10 at 18:33
  • The statement that a Customer object is the same as a Product if they exhibit the same behavior will lead one down a slippery slope. The point of having a good model is to grow and handle complexity over time. Just because two distinct concepts of the domain have similar behavior today does not mean they can be clumped together. That's too narrow a view for application development. – Subhash Bhushan Sep 10 at 18:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.