Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Occasionally connected CQRS system

Problem:

Two employees (A & B) go off-line at the same time while editing customer #123, say version #20, and while off-line continue making changes...

Scenarios:

1 - The two employees edit customer #123 and make changes to one or more identical attributes.

2 - The two employees edit customer #123 but DO NOT make the same changes (they cross each other without touching).

... they then both come back on-line, first employee A appends, thereby changing the customer to version #21, then employee B, still on version #20

Questions:

Who's changes do we keep in scenario 1?

Can we do a merge in scenario 2, how?

Context:

1 - CQRS + Event Sourcing style system

2 - Use Event Sourcing Db as a Queue

3 - Eventual Consistency on Read Model

4 - RESTful APIs

diagram for the visually inclined; it's mash-up of a MS diagram and a few things changed

EDIT-1: Clarifications based on the answers so far:

In order to perform fined grained merging, I'll need to have one command for each of field in a form for example?

enter image description here

Above, finely grained commands for ChangeName, ChangeSupplier, ChangeDescription, etc., each with their own timestamp would allow for auto-merging in the event A & B both updated ChangedName?

Edit-2: Follow up based on the the use of a particular event store:

It seems as though I'll make use of @GetEventStore for the persistence of my event streams.

They make use of Optimistic Concurrency as follows:

  • Each event in a stream increments stream version by 1

  • Writes can specify an expected version, making use of the ES-ExpectedVersion header on writers

    • -1 specifies stream should not already exist

    • 0 and above specifies a stream version

    • Writes will fail if the stream is not at the version, you either retry with a new expected version number or you reprocessed the behavior and decided it's OK if you so choose.

  • If no ES-Expected Version specified, optimistic concurrency control is disabled

  • In this context, the Optimistic Concurrency is not only based on the Message ID, but also on the Event #

like image 613
Aaron Avatar asked Aug 09 '14 20:08

Aaron


People also ask

What is the difference between CQRS and event sourcing?

CQRS is implemented by a separation of responsibilities between commands and queries, and event sourcing is implemented by using the sequence of events to track changes in data.

Is CQRS asynchronous?

It's always in-sync. CQRS is rarely synchronous because we want to work/scale with different resources, type of databases, use a messaging infrastructure: we can rarely make a distributed transaction cross-resources (we could talk about XA or Sagas but… not now!).

Is CQRS event driven?

One of the most popular practices in event-driven architectures today is called CQRS, which is short for Command Query Responsibility Segregation. CQRS is a style of architecture that allows you to use different models to update and read domain data.

What is CQRS good for?

CQRS is a popular architecture pattern because it addresses a common problem to most enterprise applications. Separating write behavior from read behavior, which the essence of the CQRS architectural pattern, provides stability and scalability to enterprise applications while also improving overall performance.


1 Answers

If I understand your design picture correctly, then the occasionally connected users enqueue commands, i.e., change requests, and when the user reconnects the queued commands are sent together; there is only one database authority (that the command handlers query to load the most recent versions of their aggretates); only the view model is synced to the clients.

In this setup, Scenario 2 is trivially auto-merged by your design, if you choose your commands wisely, read: make them fine-grained: For every possible change, choose one command. Then, on re-connection of the client, the commands are processed in any order, but since they only affect disjunct fields, there is no problem:

  1. Customer is at v20.
  2. A is offline, edits changes against stale model of v20.
  3. B is offline, edits changes against stale model of v20.
  4. A comes online, batch sends an queued ChangeName command, the Customer of v20 is loaded and persisted as v21.
  5. B comes online, batch sends an queued ChangeAddress command, the Customer of v21 is loaded and persisted as v22.
  6. The database contains the user with their correct name and address, as expected.

In Scenario 1, with this setup, both employees will overwrite the other employees' changes:

  1. Customer is at v20.
  2. A is offline, edits changes against stale model of v20.
  3. B is offline, edits changes against stale model of v20.
  4. A comes online, batch sends an queued ChangeName command to "John Doe", the Customer of v20 is loaded and persisted as v21 with name "John Doe"
  5. B comes online, batch sends an queued ChangeName command to "Joan d'Arc", the Customer of v21 (named "John Doe") is loaded and persisted as v22 (with name "Joan d'Arc').
  6. Database contains a user with name "Joan d'Arc".

If B comes online before A, then it's vice versa:

  1. Customer is at v20.
  2. A is offline, edits changes against stale model of v20.
  3. B is offline, edits changes against stale model of v20.
  4. B comes online, batch sends an queued ChangeName command to "Joan d'Arc", the Customer of v20 is loaded and persisted as v21 (with name "Joan d'Arc').
  5. A comes online, batch sends an queued ChangeName command to "John Doe", the Customer of v21 is loaded and persisted as v22 with name "John Doe".
  6. Database contains a user with name "John Doe".

There are two ways to enable conflict detection:

  1. Check whether the command's creation date (i.e., the time of the employees modification) is after the last modification date of the Customer. This will disable the auto-merge feature of Scenario 2, but will give you full conflict detection against concurrent edits.
  2. Check whether the command's creation date (i.e., the time of the employees modification) is after the last modification date of the individual field of the Customer it is going to change. This will leave the auto-merge of Scenario 2 intact, but will give you auto-conflict-detection in Scenario 1.

Both are easy to implement with event sourcing (since the timestamps of the individual events in the event stream are probably known).

As for your question "Who's changes do we keep in scenario 1?" -- this depends on your business domain and its requirements.

EDIT-1: To answer on the clarification question:

Yes, you'll need one command for each field (or group of fields, respectively) that can be changed individually.

Regarding your mockup: What you are showing is a typical "CRUD" UI, i.e., multiple form fields and, e.g., one "Save" button. CQRS is usually and naturally combined with a "task based" UI, where there would be, say, the Status field be displayed (read-only), and if a user wants to change the status, one clicks, say, a "Change Status" button, which opens a dialog/new window or other UI element, where one can change the status (in web based systems, in-place-editing is also common). If you are doing a "task based" UI, where each task only affects a small subset of all fields, then finely grained commands for ChangeName, ChangeSupplier etc are a natural fit.

like image 108
Alexander Langer Avatar answered Oct 11 '22 22:10

Alexander Langer