How we leverage CQRS at Vega to build safer trading systems

When talking about the technical architecture of Vega, we quickly highlight that we’ve adopted a design pattern called Command Query Responsibility Segregation aka CQRS. I hear you saying “what does that mean?” and “why should it be of interest to me?”. I’ll try and answer those questions in this post, but first, let’s dig a little deeper into the background and history.

Barney, Dave and the team discussing Vega in London

In early 2018, when designing Vega, Dave Hrycyszyn advocated for the use of CQRS. I’d come across this pattern, but not really had chance to use it, he’d previously identified that it has great merits when used with blockchain. Thanks Dave!

Most famously attributed to the pattern is the software engineering pioneer Martin Fowler, he states the following:

“At its heart is the notion that you can use a different model to update information than the model you use to read information. For some situations, this separation can be valuable, but beware that for most systems CQRS adds risky complexity.”

This is a little different to other data patterns, for example CRUD, where users can create, read, update and delete records atomically using a single operation.

With the design for Vega, we have a consensus layer, currently provided by a Tendermint core blockchain. Typically a set of geographically distributed validator nodes provide a consistent ordered view over a set of data transactions.

The data flow in Vega leveraging CQRS referenced from the Vega Protocol Whitepaper

The BFT algorithm used in Tendermint core provides a guarantee of consistent ordering within each batch of transactions. Often in blockchain we refer to these batches as a block. All data inputs that may cause state change must go through consensus so that the network has a consistent view.

To help visualise this, take a look at the diagram above, it shows our consensus layer receiving ‘write’ transactions and outputting a set of ordered transactions ready to be processed by the Vega protocol core.

Referring back to the definition of CQRS, you can think about these messages as Commands. We have distilled all the ‘write’ operations needed for Vega into a discrete collection of protocol transaction messages. A subset of these messages can be seen below.

Collateral and Trading commands diagram referenced from the Vega Protocol Whitepaper

When a command is passed into the protocol core, it is processed and as a side effect, data state may be affected and changed. In our design, we update the state in a store and clients who wish to read the effects of the state change must do so using an API request. You can think about these ‘read’ operations as Queries. Importantly they are separated, or Segregated from all ‘write’ operations.

So “what does that mean?”, well it means that we can nicely secure our system using a Vega defined instruction set and use the power of distributed state machines to process them safely. No writes can happen without taking the form of a defined command, and reads must be strictly from stores and read API. We enforce strict separation between the consensus, application, and API layers. From our research and the working application, it’s clear that a distributed, decentralised trading platform like Vega is an ideal candidate for CQRS.

For example, we can be sure that only order instructions received and agreed on by the consensus layer are processed by the core engines and affect the collateral or trades in the system. The same can be said for voting on markets or other governance.

Oh yeah and “why should it be of interest to me?”, if you’re designing a transactional, blockchain based application and need to safely separate your ‘write’ operations from your ‘read’ operations then it can lead to a much clearer and safer way to control the representation of the information in your system.

Feel free to reach out to me me if you’d like to know more or fancy chatting/debugging your designs.

3 Likes

<3 I love CQRS as it enables simple cross-platform (or cross-chain) interoperability. This one design choice makes it simple to tie dissimilar API’s and other systems together. This is the main reason we can do blockchain integration without making crazy opcode-level calls all day long.

1 Like