Closed w00519772 closed 5 years ago
We're not using the full CQRS approach of having different databases/stores for reads-database or writes-database. CQRS just means separation between reads/queries and writes/transactions. In many cases you don't need to have different stores/databases as that is a further complexity you might not need (In some cases you might need it, but we are not showcasing those cases because are kind of more 'rare' cases).
We do CQRS(what I call simplified CQRS) in the Ordering microservice: Queries on one hand and transactions triggered by Commands and Mediator pattern, but both are working, in this case, against the same SQL database.
Not sure I understand your last question. An 'Integration Event' should be used to integrate other microservices or external applications, not to be used just by a single microservice. If you want to use events within a single microservice, use "Domain Events", also explained in the related book/guide: https://aka.ms/microservicesebook
Hope it helps. 👍
If you want to use events within a single microservice, use "Domain Events", also explained in the related book/guide: https://aka.ms/microservicesebook Hope it helps.
@CESARDELATORRE, thank you for replying.
I realise you're not using the full CQRS approach. However, say I wanted to follow the full CQRS approach. I would have to make sure the read model and write model are in sync.
Your OrderContext.SaveEntitiesAsync looks like this:
public async Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
await _mediator.DispatchDomainEventsAsync(this);
var result = await base.SaveChangesAsync();
return true;
}
If I use a Domain Event as you suggest then my Domain Event Handler would contain code like this:
mongoCollection.Save(object);
What happens if the server crashes immediately after calling: mongoCollection.Save(object)? It would mean the record exists in the read database (MongoDB), however it does not exist in the write database (SQL Server)? The write database should be the single source of truth (I believe).
I am wandering if I need to create a message queue/bus inside the microservice. If I did this then there would be one message bus for communication between Microservices (via integration events) and another message queue/bus inside the Microservice to keep the read model and write model in sync.
Thanks again.
Hi @w00519772
My approach would be something like following, Assuming an event SomethingHappenedEvent is raised:
Of course there is a period of time, when write model is updated and SomethingHappenedEventProcessed is not still processed, so Read and Write model are not in sync. Same could happen if step 4 fails and needs to be retried. You have to deal whis this :)
Note that event handler in 3 and in 5 do not need to belong to the same microservice (it depends on how you choose your microservice granularity).
Same could happen if step 4 fails and needs to be retried. You have to deal whis this :)
Thanks for replying eiximenis. How would I deal with this? (that is the point of the question) I can think of lots of ways:
1) The microservice has its own durable message queue. There is a queue for writes and a queue for reads. 2) A background task runs periodically to look for missing events.
What is the standard way of approaching this (if there is one)?
For full CQRS with two databases is perfectly fine (and safer) to use the Event Bus based on durable messages in RabbitMQ or Azure Service Bus or higher lever service buses like NSERVICEBUS. In order to handle issues like one message not processed or properly sent, you need to use patterns like “Outbox pattern”. We have that in eShopOnContainers with the EventLog. An additional background task could be looking for unprocessed events. Note that higher level service buses like NServiceBus already provide these patterns out of the box. NServiceBus was built by Udi Dahan and his team specifically with full CQRS in mind. Finally, another approach is to just use an EventStore if you also want to do Event Sourcing, which is a related pattern.
In order to handle issues like one message not processed or properly sent, you need to use patterns like “Outbox pattern”.
Thanks again. I am almost there. I will do some more reading about the Outbox pattern.
My understanding is that the write model is updated and an event is published to update the read model.
Q1) Is the event published to update the read model an integration event (as opposed to a domain event)? Q2) Is the read model infrastructure code (repositories, context etc) and write model infrastructure code contained in the same microservice? I believe it is. Q3) There is an event bus that is used to integrate microservices/other applications: https://github.com/dotnet-architecture/eShopOnContainers/tree/dev/src/BuildingBlocks/EventBus. Is this bus used to publish and subscribe to CQRS events or would you create another event bus inside the microservice for this?
For full CQRS with multiple databases, use Integration Events. That gives you more flexibility to have a different service in charge of processing the integration events. You could have and I recommend two different services for this, although from a concept perspective are the same “Business Microservice” or Bounded Context, similar to the Ordering microservice which is composed my several physical services. You can use the same Event Bus for integrating a “Reads-Database”. It is exactly the same concept we use but integrating different microservices.
You could have and I recommend two different services for this, although from a concept perspective are the same “Business Microservice” or Bounded Context
If you were to do this with the Ordering Microservice then I assume you would have separate projects for:
Ordering.Read.API Ordering.ReadModel Ordering.Write.API Ordering.Domain Ordering.Read.Infrastructure Ordering.Write.Infrastructure
Is that right?
, similar to the Ordering microservice which is composed my several physical services.
The Ordering Microservice is made up of three services i.e. API; Background Tasks and Signal R. Is that what you mean by: "composed my several physical service"?
Finally, would the read model
1) contain the same fields as the write model (represented differently of course) or 2) contain different fields i.e. pick and choose what fields to include in the read model and which to include in the write model - similar to what you have done with the Location Repository i.e. there could be fields contained in the write model that do not exist in the read model.
Thanks for answering. I have experience with CQRS, but not really where there are two separate databases.
Not exactly. Usually the complexity of internal DDD patterns are only needed for the transactional area which is the “Writes” area.
So, probably something like: Ordering-WRITES/TRANSACTIONAL
Ordering-READS/QUERIES side
QUESTION: The Ordering Microservice is made up of three services i.e. API; Background Tasks and Signal R. Is that what you mean by: "composed my several physical service". --> YES
QUESTION: The Ordering Microservice is made up of three services i.e. API; Background Tasks and Signal R. Is that what you mean by: "composed my several physical service". --> YES
QUESTION: Finally, would the read model
Hope I helps, Cesar
We have that in eShopOnContainers with the EventLog
Thanks again. Could you elaborate on that please? Perhaps refer to some code.
It looks like the conversation ended, so I'm closing this issue now. Feel free to comment, will reopen if needed.
Thank you for developing eShopOnContainers - it is a very helpful reference application.
I am trying to understand how this architecture works with CQRS when there is a write store e.g. event store or sql server etc and a read store e.g. MongoDB.
Is it "acceptable" for a Microservice e.g. 'Microservice A' to publish an integration event and then handle the integration event (when there is no other Microservice that handles it)?