Here I will try to describe the concept of CQRS without event sourcing based on some other principle instead. The concept to organise your data and messages to create scalable and reliable CQRS/Messaging-based architecture without additional complexity from event store.
At first here is small introduction on what SQRS is. I suppose you are familiar with MVC model and use it on daily basis. And i suppose you understand benefits of separation your code into separate modules.
CQRS – Command-Query Responsibility Segregation. CQRS just separates model into 2 separate parts – READS model and WRITES model. They also can be referenced as Query model and Command model.
You might find some more complex definitions all over the internet, but actually all other patterns are additional. Just separating model into queries and commands makes it CQRS. Segregation must be clean so commands can’t query data.
The most natural addition to CQRS is eventual model (but even this is not obligation). When you send command you expect to dive into asynchronous world because processing could take some time on server. And beside ‘async’ lets remember some other words – distributed systems, cloud synchronisation, sharing, eventual consistency, concurrent access. Even if you think that now nothing from this list is close to your application’s field – very soon it will be there.
So i say yes to messaging: you can add persistent queues of messages into CQRS scheme.
Serialized commands go into message queues to different locations – it can be local cache or several services on server side. Message queues can have local journaling to prevent message loss. For example, if you send data to server and there was no connection – the messages will be stored locally. And finally when you restart the application the queue will be reexecuted.
Cache db / reporting database (this could be some key-value storage or just memory cache inside client application) is primary source for query interface. When you pass some command for execution you can inform cache simultaneously (also through event queue) that you have some changes coming.
EVENT SOURCING – CQRS/ES
The most documents about SQRS refer to Event Sourcing as must-have feature in additional to SQRS. I don’t think so. This concept fits well into eventual model, but if we affect ‘client state’ of application (ui, inner cache, etc) only through event listener – its not yet Event Sourcing. Even more – “event sourcing is a completely orthogonal concept to CQRS” (source). Definition is the following:
Storing all the changes (events) to the system, rather than just its current state.
So this is method of storing business data as sequence of modification events. For some cases its very useful as you can restore object state at any point in history but generally its not must-have feature. This is actually the problem of how to store your business data. And decision use it or not comes from business domain. There is considerable overhead from using it as you have to restore final state for each object every time, you need more storage to keep all events and processing inside query model will be more complex to maintain. Often you need to make snapshots to make it run fast enough.
At first you think that event sourcing is simple and easy model – but in most cases it is ugly way (imho). When you interact with objets in real world you don’t need whole history on each of them. Only one exception is finance transaction list from your credit card – but its rare case. The most of your objects don’t require stored events of modification as separate instances (we still can store this information in some logs).
There was some discussion on distributed podcast telling CQRS might bring +30% of developing time and sure this is critical if there are no really large benefits from it. BUT, Imho, all this talks are more about EVENT SOURCING. Implementation of basic CQRS is not so complicated (and you event don’t have to use messaging), but the switch to ‘data combined from events’ concept is much more complex for your developers to handle. So the idea is to replace event sourcing principle with some other simple concept to make life easy.
But what instead of event sourcing? Im going to present some solution here but first lets review what problems we are going to solve. I see now (2014) that basic scheme of interaction for client-server application is evolving to this:
Now, instead of simple client-server pair, we have several client devices with ability to sync data and several nodes of distributed server system.
Here we can make one more crucial decision – decide where your system stands in CAP theorem – AP or CP. This is a business choice. And you can still do CQRS in full CP way, but i think today Eventual consistent systems are more attractive. So here we are talking about them. Note that on server side of cluster still can be CP parts – like etcd syncing of cluster configuration or MongoDb sharded cluster.
I will go even further – lets assume we have 2 datacenters and replicate data between them (you can just create 2 droplets in digital ocean to emulate this situation without any effort). Lets assume you have mongo cluster as storage in each of them to hold your data.
After submitting some command we need to pass it as message through various channels to local cache, server 1 and server 2. As we do this asynchronously the following problems may arise here:
- Message loss
- Message duplication
- Message reorder
- Concurrent access conflicts
These are common problems when you are talking about messaging. Lets introduce additional requirements:
- After offline work the changes should be synchronised with servers and other clients
- Multiple clients should be notified the fastest possible way
- Messages from client should go to server side through 2 different routes simultaneously
- Local cache should receive some updates immediately and if command will fail on server side additional notification message should cancel it properly
You can imagine the set of problems which may arise here if you handle database updates the usual way. Plus, you have to keep in mind the following picture – in real world situation might get worse…
Pretty scary… Lets introduce some concept which will handle most of declared problems implicitly.
DATA SEGREGATION BY RESPONSIBILITY or SIGNED-DOCUMENTS MESSAGING CONCEPT for CQRS
The next part is a concept which may be not new but i failed to find it as common knowledge related to CQRS.
As solution we need to use some messaging principle similar to event sourcing (and combine it with messaging transport layer based on ZeroMQ, NanoMsg or similar).
Event sourcing principle – each object is composition of modification events.
Alternative principle – each object is composition of immutable parts exclusively signed (owned) by specified users. Each immutable part has ‘a sign’ – time and user signature indicating ownership. Each message replaces corresponding immutable part completely (and of course there are no delete operations – only is_deleted flag inside structure).
Analogy from real life – you represent some business solutions as series of signed documents from various departments. At any moment you can replace one document by another, but you can’t modify document once it was signed. You can send any amount of copies of such documents to anyone without any collision. Missing or reordered docs are also not a problem in such scheme – to perform some action you need the list of actual documents and if some of them are missing or invalid we can request them again from owner.
In real life we have documents which are signed by several persons. Actually this documents either can be split in separate signed parts or ownership can be transferred from one person to another when first person can no longer access first object. So we have one document – single person responsibility. (Here we talk of document as object part).
This concept is similar to functional programming where you deal with immutable objects. You can also treat sign as revision marker of version control system. If existing sign is newer you can’t overwrite the document by previous version by mistake. So before each database write interaction you are checking signs.
You could also treat parts as svn files. And as you might guess the main problem is how to solve concurrent commits? In ownership scheme you can successfully commit only by grabbing ownership (and sign document with another signature). In simple case you just use the latest validated version of committed document.
In special cases when the group of users can modify the document at the same time (the case you prefer to avoid in real life because this is complex problem how to merge conflict changes in such situation). Here we have 2 subcases:
- The document is big and modification time is considerable. Imagine google docs here. The simple solution is to have exclusive temporary lock on document. During edit process the lock can be extended. Additionally, the group of users can be notified of changes if its needed.
- Second case – you have large set of data and set of users who can operate on such data simultaneously. Here you can set additional restrictions for collection updates. At start of modification client must request new cache (this can be done async way) and when you send message with modified version of data it contains sign of previous source cache. Trying to overwrite more recent data will produce error message. Trying to make overwrites based on outdated cache will also produce error message.
If during your business process some objects pass from one user to another you can add additional inner parts to object signed by different users instead of signing whole object data. For example, if your product needs some verification from someone instead of is_verified flag inside main object data you add new part inside object composition: verification + sign.
Whole concept can called: treat your data as composition of signed documents. But keep in mind that signed here are parts of business objects. And they can be very small. Even if its only one integer field, but by business process it should be assigned by different person, there is a sense to separate it into another part to make things clear. Also it make sense in terms of security.
The last thing is additional ‘second-order objects‘ as i call them. Those are the result of some minor map-reduce operations on first-order data. Counters, statistics, summaries, distributions, short representations, views, caches… all such objects are kind of cache. They have no signs (and its logical as it is the result of composition of signed objects).
EVENT MODIFICATIONS / REFACTORING
I think the approach when you are cloning events and make new name to each new version is ugly (like EVENT_MAKE_PURCHASE_V14 -> EVENT_MAKE_PURCHASE_V15, etc). But you have to use such approach only when you are working with event sourcing. If you are not limited to event sourcing you can just make your message handler be aware of possible missing fields (or even add version inside message structure). In other worlds handler should just convert message to the most recent known version in the most common cases.
So there are no additional issues here.
One more term you can often hear when CQRS is mentioned: Saga – business process manager handling reversible transaction chain. Inside saga there is some state machine which controls workflow (in ideal case). This solution provides additional protection from message ordering/missing/duplication problems inside world of messages.
At simple approach saga stays on client side and related messages contain saga’s id to pass messages into it. Its like mini-controller containing internal saga state. In some cases this approach is good, but i prefer to embed state into the message when it is possible to make more functional-style processing. Of course, for some cases you have to check the state transition at server side to prevent exploits.
I think its not so easy to predict if your business processes are sagas or will become sagas in future – but describing them as finite state machine is a good idea. Anyway this is also additional part to CQRS but is not required. And implementation may vary too – you don’t have to create separate class for each saga, you just have to make obvious message chains.
DOCUMENT ORIENTED STORAGE
As you treat your data as composition of documents some NOSQL document-oriented storage is the most natural solution here. And you can contain all signed parts inside one nosql-document. The sings can be stored as expanded form to simplify filter/search operations. As primitive example you could have the following for user:
name: "Jack (Admin 145)",
You can see two parts here – user data entered by user and role given by some administrator.
Disadvantages of signed-documents approach
- A bit more traffic is required (not in comparison to event sourcing, but to general 3-tier)
- Slightly different view at data storage is required from your developers
Advantages of signed-documents approach
- Implicit solution for all basic ‘messaging’ problems
- Simple principle of data management which is close to actual business flow (signed documents)
- Immutable parts give more consistency and less message implementation chaos
- Sign validation and responsibility segregation give ability to track user influence and maintain data order (access restrictions from design)
- High-concurrency access problems are solved with sign locks and cache actualisation settings.
- After implementation functions related to signing and sign verifications/locks, approach induces minimal additional complexity – splitting your documents into parts gives you only benefits (especially when you can still grab them whole using nosql solution)
SUMMARY – WHAT TO DO
To summarise it all – instead of event sourcing we can use different concept – document-signing approach and the following rules:
- Split your database objects not into composition of past events, but into composition of signed documents (divide objects by ownership and responsibility).
- Treat objects parts as immutable record which can only be replaced completely.
- Send whole signed immutable part inside each message which can affect them.
- Each immutable part has a sign which contains time/user/sender/lock informations and is used to resolve all message collisions.
- Extract counters and other second-order data into cache and regenerate it if composition of immutable parts has changed (or upon request).
- Think of your data as composition of signed business documents.
- In need of high-concurrent access impose additional sign and cache actualisation requirements.
I could also name this as Data Segregation by Responsibility – CQRS/DSR. This approach makes CQRS messaging easy.
This document can be modified to reflect my new experience and can be extended with new samples. Any thoughts are welcome in comments.