Hi Steven.
Thanks for your patience to understand my questions and doubt.
I also consider this forum as knowledge sharing about DDD and CQRS,
and ofc about Axon, great framework that helps a lot in implementing
this principles, so there is no problem in telling me I'm wrong in
anything, ofc ;) Thanks for sharing training details, I'll check with
management our options to meet these, please do not feel obligated to
answer all my questions if it goes beneath regular support that you give
on this forum ;)
If it is ok, I'll try to clarify further what remains here to get on same page. ;) I
think that root of my problem is in understanding the 'philosophy' part,
so I'll focus more on this.
Well, yes, within Saga I had intention to query aggregate, but strictly on given aggregate ID -> so basically to call load on this aggregate event sourced repository. It was not my conscious decision not to follow the CQRS ;)
Because, I had feeling that this was allowed to do within Saga -> and still not to violate CQRS principle? But ok, I see now from your answers that this is not something that should be practiced. But I still do not read clearly exact alternative that should be followed then.
To help putting that on clear path, I'll try to put that my example shortest/clearest/concrete as possible as:
Lets assume we have 2 aggregates, an Building, and an Tenant. Tenant applies for apartment within Building, identified by some apartment number exposed via Building api, and apartment is entity within Building identified internally via some UUID like string Id. (Btw, we use typed ids, wrapped around generated UUID string ids).
When that apartment application event is applied, we would like to start Conversation (another aggregate) between Tenant and Building manager (yet another aggregate), that mentions building address and apartment details, as well as some Tenant details like name and title. For starting conversation we use saga, since that conversation lasts until application is eventually approved (or refused) by building manager. Now lets keep these other aggregates out of example here, to make it simpler ;). So, my point is, I need details from tenant and details from building/apartment, and I can't have both in single event. And I need them to construct new command, within Saga. So, I'm on command side, and yes, I need some kind of query mechanism, I think? ;)
From your previous answer I also read that since all the truth is
already in events (since we do event sourcing), all the saga needs is to listen to already available
events to construct all the data it needs. But, problem is that in this
saga we start listening only upon specific event is applied (TenantAppliedForApartmentEvent),
and therefore we couldn't listen directly to already played events in
past that were adding building address or other apartment details.
Wouldn't load query to event sourced repository of building for
example, indirectly do that actually, for given building aggregated id?
Maybe I miss some concept that helps with that?
As only alternative that I see, if I understood you, all these details that are needed to compose the conversation should be available within events? It is not enough to just raise event like this one:
@Value
public class TenantAppliedForApartmentEvent {
public static final String APARTMENT_ID = "apartmentId";
BuildingId buildingId;
ApartmentId apartmentId;
TenantId tenantId;
}
... and then ask for more details within saga (as I gave example in my first code snippet within first post in this question topic).
Instead,
1. we should give more details within event itself (for example, since this event is being raised by Building aggregate, we should also put entire Building address and apartment details). Anyways, since we do not have enough details for Tenant, then we create new command within Saga (that looks like query), like:
ObtainTenantDetails(tenantId)
2. that command is handled by Tenant aggregate (that does not mutate tenant state, but just applies new event like:
TenantDetailsObtained(tenantId, tenantName, tenantTitle, tenantEmail)
3. that event is handled by our saga, that collects all needed details to compose needed conversation message, at that point.
That way we do not couple Saga to aggregate types, so saga is standalone, but we for sure add more coupling to events and commands that are created for purpose of this particular logic. And in future, lets say, we want to include into our message details like apartment energy label. That data is not in our events, since we can't foresee everything upfront, ofc. So, at that point we would need one more command and event, or to modify some existing event -> meaning it would also change dependency of saga to these existing events that are already wrapped withing aggregates code.
Is that the way how it is intended to be used, but not to violate CQRS principles? Probably my aggregates need too much internal data of each other -> but we are just exploring the DDD and CQRS and Axon to learn it, on such pet project. Anyways, it seems to me that such scenario should happen a lot in some real systems.
I think it would help me a lot to understand this better if I get the answer where exactly it is
ok to use
load by Id method of event sourced repository, and not to break CQRS? On query side we can just use regular queries via QueryGateway, we do not need it there?
Hope that I succeed in making my question clear this time, and that you had patience to read it - hopefully with some answer for me ;)
Cheers, Vladimir.