How to achieve full consistency between read (query) model and write(command) model

574 views
Skip to first unread message

Rohit Singh

unread,
Sep 23, 2020, 1:41:47 PM9/23/20
to Axon Framework Users
Hi,

Scenario:
I have 2 spring-boot services acting as command and query as below.
  1.  Command-Service: Responsible for handling commands, events, maintaining aggregate's state and storing data into database.
  2.  Query-Service: Responsible for handling queries in order to fetch data from databse.
Please note that both services are sharing the same instance of axon-server and also the same databse(in this case ElsaticSearch).

My UI is designed in such a way that when user submits the form to create data, an ajax call happens which in turn performs 2 rest calls in below order: 
1. First call is a post request which goes to Command-Service which performs below:
  • Creates a CreateDataCommand object with formdata and fires the same as below: org.axonframework.commandhandling.gateway.CommandGateway.sendAndWait( CreateDataCommand).
  • The CreateDataCommand is handled in the aggregate which fires DataCreatedEvent:
    @CommandHandler
    private DataAggregate(CreateDataCommand createDataCommand  ) {
       AggregateLifecycle.apply(new  DataCreatedEvent ());
    }
  • DataCreatedEvent is handled at 2 places,
    1. In the Aggraget itself, where the Aggregate updates its state:
        @EventSourcingHandler
        private void on(AppCreatedEvent appCreatedEvent) {
           // updated aggregate's state
        }
    2. In the Projection, Which saves data into the database.
        @EventHandler
        private void on(AppCreatedEvent appCreatedEvent) {
           // Saves data to database.
        } 
2. Upon sucessful execution of above the ajax immediately sends a get request to Query-Service which performs below:
  • Fires  GetDataQuery query as below: org.axonframework.queryhandling.QueryGateway.query(GetDataQuery, ResponseType).
  • The  GetDataQuery is handled to fetch data from database as below:
    @QueryHandler
    List<Data> getData(GetDataQuery getDataQuery){
      // hits the databse and returns data.


Problem:
Even though the data is being saved into the database by Command-Service, the call to Query-Service is triggred and it always returns empty list. On second call onwards it returns the correct data. This is happening because Query-Service hits the database before Command-Service finishes.
While going through some posts on the forum i learnt that  axon never wait for an event being handled when sending a command. The responsibility of the command stops at publishing the results of that command (i.e. events).

Can someone help me in achieving the full consistency or find a way to eradicate the side effects that are occurring on account of above phenomenon.

Steven van Beelen

unread,
Sep 24, 2020, 9:07:59 AM9/24/20
to Axon Framework Users
Hi Rohit,

First and foremost I feel obliged to tell you this mailing list will be discontinued as of the 28th of September 2020.
Instead, we've set up https://discuss.axoniq.io/, as it provides us a better means to help anyone who's searching for Axon information.
On top of that, the forum style should promote a more free format so that other users (outside of the AxonIQ work pool) can also help out people.

Having said that, let us move over to your consistency question.
Using a message driven set up like this, embracing CQRS so to say, somewhat enforces you to cope with the eventual consistency you are pointing out.
The most optimal way to deal with this, is to embrace that fact exactly, and deal with information retrieval in a different way.

Thus instead of immediately performing a query after a command, you should subscribe yourself to the results of the updates on the query model.
You could for example build a solution based on WebSockets where you subscribe to a given model.
Use SSE to share the fact the data is present.
Or use the more recent Reactive movement, and utilizes Mono's and Flux' to tap into the results as they go.

Especially the latter is something Axon supports more directly with it's subscription query, and more recently as well with the Reactor Extension.

Hope this helps you further Rohit!

Cheers,
Steven



--
👇👇👇👇👇👇👇👇👇👇
 
❗ Please note that this mailing list will be discontinued on September 28 ❗
We are moving to https://discuss.axoniq.io/ and we do hope you'll join us there.
For more details please see https://groups.google.com/g/axonframework/c/V77Wq9g2HLU
 
👆👆👆👆👆👆👆👆👆👆
---
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/axonframework/00aaff48-f27a-497d-b298-910c810a9a67n%40googlegroups.com.

Rohit Singh

unread,
Sep 28, 2020, 12:28:16 AM9/28/20
to Axon Framework Users

Hi  Steven, 

Thanks for the response.

Can i use something like org.axonframework.extensions.reactor.queryhandling.gateway.ReactorQueryGateway.subscriptionQuery(FindDataByIdQuery, ResponseType<Data>, ResponseType<Data>).repeatWhen() inside the Command-Service to check whether the data has been created in the database or not and will return success response to ajax only if the data is created.

please reply.

thanks 

Steven van Beelen

unread,
Nov 2, 2020, 4:26:50 AM11/2/20
to Axon Framework Users
Hi Rohit,

Whether to use `repeatWhen` is out of perspective here really.
What you should do is (1) make the subscription, (2) ignore the initial result (as there can be none), (3) wait for the results of your updates in the Flux and then (4) dispatch the command.
You can check out how my colleague Stefan views this in this sample controller.

I do want to strongly emphasize what I said earlier though:


Having said that, let us move over to your consistency question.
Using a message driven set up like this, embracing CQRS so to say, somewhat enforces you to cope with the eventual consistency you are pointing out.
The most optimal way to deal with this, is to embrace that fact exactly, and deal with information retrieval in a different way.

Trusting this suffices for the time being.

Cheers,

Steven van Beelen
Axon Framework Lead Developer

twitter-icon_128x128.png

Reply all
Reply to author
Forward
0 new messages