Youtube videos - Why is a projection, query or not, modelled as a subpackage of the domain?

I’ve been wrestling with the structure of my project, so I viewed a number of the videos on youtube, but that only increased my confusion.

CQRS is about segregating the command and queries. So I would expect one domain model, with commands and events, and then N projections. A query is based on a projection, in the example video (The Query Model - coding example - YouTube) it requires building up an entity structure.

In the example, the code is organized as:

  • domain/query

Or

  • domain/projection1

That does not make sense to me, personally I would organize the code as:

  • domain
  • projection1
  • projection2

Why is a projection, query or not, modelled as a subpackage of the domain?

Secondly, coming to my actual question, suppose one of the projections is used to provide for a rest interface, and another for a web application. Would you place all stuff relevant to that into the projection, or does the projection only contain the model? So:

  • domain
  • restProjection/model
  • restProjection/restEndpoint
  • webappProjection/model
  • webappProjection/Controller

Or

  • domain
  • restProjection/model
  • webappProjection/model
  • infrastructure/rest/endpoint
  • infrastructure/webapp/Controller

To give my two cents as the creator of that video:

Why is a projection, query or not, modelled as a subpackage of the domain?

The projection is, in my mind, just as well a part of your domain.
It just projects the state differently and for a different target audience.

Would you place all stuff relevant to that into the projection, or does the projection only contain the model?

In all honesty, this is up to your preference I’d say.
I do have an opinion on the matter, though.

The intent of directory structure, first and foremost, is to enforce a form of separation between the components.
As such, it’s beneficial to not make projectors (the classes handling events, projecting it as state, and answering queries) public, for example.

Having said that, I would opt for your second package structure over option one.
If that data is exposed through a controller, queue, topic, etc, is not a concern of the projection in question, nor that specific part of your domain.

This is what it has query handlers for.
You can have the bus figure out how to get there, whereas your components can stick to their intent.
Which is, projecting the data and storing it, one way or another.

So, there’s my two cents!
Let me know what you think, @tbee.

Thanks for answering Steven, I was on a little vacation that is why I’m reacting a bit late.

Interesting how opinions can differ; you’re organizing around technical aspects (projection, infrastructure), while I personally prefer “encapsulation” - everything around the webapp is placed together, in order to minimize cross context relations.

So, you feel the projection is part of the webapp then, right?

I wouldn’t necessarily make that assumption myself.
The projection is just a form of the Domain Model, dedicated to the Query Side.
That’s not something that is exposed over the UI (or a bus, RSocket, gRPC, whatever you prefer) at all, as those are internals to your application nobody (no API) should care about.

Instead, you’d have queries targeted towards the projector (i.e. the component handling events to project them into your query model/projection). The Query Handlers inside the projector from there filter or map the right state from the model. And the webapp-side will in turn dispatch those queries, thus using the queries and the query responses (which are part of the coreapi package) as the communication means.

I feel that this way there’s a clean separation between your coreapi, the command-side of the application, the query-side of the application, and any form of outward-facing communication layer you’d create.

Or, am I making a wrong assumption on your statement there, Tom?

No, you’re not. I see the projection as an optimized model specifically for the webapp. If it weren’t for the webapp, I would not have written the projection. It’s not like “hey, that would be a good projection to build, maybe someone will use it”. The projection has a reason to exist.

And because the projection is written for the webapp, it should be discouraged from being used by other contexts. Therefore it is part of the web application package.

That said, I’m still struggling with this approach. But that has to do that events updating the projection, also are forwarded to the web frontend to update something on the screen. Two event listeners that run in parallel, resulting in redundant information in the projection not being up to date yet.

I gather you’re not using Query Messages and Query Response Messages at all then, right Tom?
So, exposing the storage solution to the webapp side of your application in fact, right?

You would mitigate this by using queries between these.
So, in essence, defining an API that if you look at CQRS would be a fair abstraction to keep a separation.

It’s true of course Query Models are optimized for their purposes, and that these can go the stretch to serve a single purpose. But whether that makes it fair game to combine the storage solution with the webapp package in your example, not sure about that.

To refer back to your previous comment:

The shared approach in the videos only uses the messages (the coreapi) as the shared component. Wouldn’t you argue that’s just as much a form of encapsulation? Just as much minimizing cross-context relations, since the only communication point are the commands, events, and queries? I’d wager calling a package webapp just as much sounds like a technical aspect.

Please correct me where I’m making wrong assumptions by the way! Discussions like this is what improves me, so I enjoy them. :slight_smile:

There would still be a disjoint between the moment the web application requests an update and the moment the same event was processed in the projection. But because of network delay that would be a much lower chance. But it still is not conceptual correct. Or is it?

The solutions I see ATM is to either move the projection into the webapp, by forwarding all events via a websocket and build a state in the browser. Or have an additional not persisted event like “DayTotalChanged”. In the current tool stack I’m leaning towards the latter, but that would require a second (non persisting) message bus.

Yes, but the main difference is that for commands and events the domain is the owner.
Queries are created to fulfill what the using application needs, so they determine how they should look. In my vision the part of the code that has the highest stakes, highest level of ownership, should contain the code. It’s like an SQL select statement, where does that belong? In the database server via a view, or in the client application who knows what columns it needs?

What I’ve done in the past (talking about Axon Framework 2 here), was implement the STOMP protocol over WebSockets. The Query Model for the UI was the page’s JSON stored per model identifier. The application’s Event Handlers in turn updated the model, and automatically pushed the update to whomever was subscribed to that specific model identifier.

For us, this worked, but it was quite some custom code to get going.
You can achieve a similar solution by using the QueryGateway#subcriptionQuery. When using the Subscription Query support, you receive a Mono for the initial result and a Flux of all the updates that follow. Through this solution you can achieve a similar format as I shared in my first paragraph.

Both cases solve the eventual consistency we need to deal with, by not actively fetching but by being kept up to date automatically.
Surely, you can share the event as-is with the front-end too. Roughly equal to the above, but you’d use the events instead of queries or the projection.

Firstly, thanks for sharing Tom!
I have a follow-up question based on this though.

What if the format of the query response / projection is required by different applications? Would share the datasource in that case? And, how would you deal with the possibility that several sources are capable of answering that question for an application? Would that “package/module” be in charge of N types of projections for that purpose?

The is exactly what I am doing. So the webpage initially builds up using placeholders ("?"), then upon websocket connect the backend send fake events that normally occur during interaction to replace the placeholders. So:

  1. construct HTML with placeholders
  2. connect websocket
  3. upon websocket connect, backend sends many “AllocationSet to A for person X on shift Y” events
  4. If the user changes a shift allocation, the webbrowser sends the “SetAllocation …” command to the backend
  5. Browser receives “AllocationSet …” event (same event as upon websocket connect).

I’ve solved the DayTotal issue by using the appplication event bus. I’m curious how this will pan out if I keep refining this approach.

Okay. So the question is here is what is more important, the concept of DRY or the concept of encapsulation? If DRY is important, then every SQL select statement should be a view, because maybe another application wants to do the same query. But that would make the database unmanageable quickly.

You can of course create a single query and reuse it between apps, but how do you know a query has become stale? Is not used by any application anymore? Axon has the advantage of being able to check for usage of the Query class, but that lasts while you stick to Java code. The minute you put in behind a REST API that advantage is lost. That is why things like GraphQL start to become popular; basicaly an SQL query from the client.

I’m not sure what you mean with the last two questions; why would i have two sources for the same question?

1 Like

Awesome, that makes total sense!
The main difference I spot is that we didn’t use events towards the UI.
We pushed delta’s of the view / query model instead.

A model as was desired by the UI.
This last point pushes me in circles, where I start moving to your end of the argument.
Need to give this some thought.

This is the freedom of choice you have when going for CQRS. You can optimize for what’s most important. If that’s quick ASAP results, then a model per query might be the way. That doesn’t mean you use it for all the models though, as the non-functional requirements per model differ. It’s just as fine to have some models focused on DRY for (I’d guess) performance reasons, as it is to use a good old RDBMS with a ton of joins because the relationships is what you need. Or that you use a graph-, time-series, or text-based-search-database.
Back to the point though, I’m not stating it for pure DRY reasons.

From the UI perspective, I catch your drift.
I’d wager my stance originates from using a global approach to any type of querying within your application, so not restricted to how REST works.

The basic example we use (within our training material for example) is that of the wish to know the price of a product. So, there’s some form of WhatIsThePriceForProductQuery out there.
This object dictates the API, which at first likely is just a productId.

A new feature request pops in, where consumers owning a form of discount/premium card get a discount automatically.
So, the userId or cardId is attached to the API in a version 2 of the application.
Another form of feature request you could think off is a general date window of sales (around Christmas for example), which would impact the price result.

One way to implement this is by adjusting the service that performs the query.
Another way to tackle this is by adding another query handler that provides a different answer to the query altogether.
The latter means you don’t have to adjust your UI component (likely a REST endpoint) to return the prices at all. Furthermore, it’s more similar to some real-world scenarios, where you’d ask a group of people/things for something, instead of always having a single authoritative unit.


Just dropping it here, but it might be nice to have a face-to-face conversation somewhere in the future… Just let me know if that’s something you’d be interested in!

But isn’t that exactly what events are? It does explain why I needed a second (not persisted) event, because the view / query model had some derived data and needed to send that to the UI as well.