The company i'm working for it's rewriting a legacy web application and we are evaluating the better architecture to get the job done.

The business domain is quite simple: there are few entities, few relationships and simple business rules.

We expect a low number of concurrent writes, but a huge number of reads: the expected read workload is much greater than the expected write workload.

The web application is meant to allow a few number of editors to create entities called blogs which are composed by posts. There are different type of posts: textual posts, photos, links to youtube videos, links to twitter posts and so on.

The expected workflow is that an editor, which is following a live sport event, is in charge of editing a dedicated blog which is basically a stream of posts for the event: the editor creates one post for each interesting fact of the event. The main concern is having each post available for the mobile and web clients as soon as possible so that people all around the world will be able to follow the event.

We have evaluated two possible architectures:

  • CQRS architecture with two different databases one for the command stack and one for the query stack
  • simpler architecture with one domain model supporting both writes and reads and one database

The main advantage of the CQRS approach is the possibility to distribute the content to the clients in an optimized way by using some dedicated read models in order to have simple and fast reads from the read model database. This way the read and write sides can be scaled independently, exploiting the difference between the write and the read workloads highlighted above.

The main advantage of the single domain model approach is a much simpler architecture. In this scenario the commands from the editors are processed synchronously and when a command is successfully processed the editor knows for sure that its work is saved inside the database and available for clients. No eventual consistency between write model and read model, no need to handle the asynchronous update of the read model from the perspective of the backoffice UI users (the editors), no need for any kind of messaging system involved in mission critical tasks.

In my opinion considering our requirements the best way to go is using a single domain model architecture and scaling the reads by using an aggressive caching strategy. The idea is using Redis as a cache in order to limit the database access and trying to update the cache layer each time we write something inside the database using a streaming approach (we will probably use Mongo DB and our first idea is exploiting the change stream feature).

Do you think that a properly sized database and a wise caching strategy with redis could be enough in order to handle our read needs ? Or conversely in a scenario where the read loads is much greater than the write loads the best way to go is using a CQRS architecture (even at the cost of a bigger overall complexity) ?

Some notes to better clarify my question

  • I mentioned Redis just as an example of a smart distributed memory cache.The overall idea is having some kind of efficient caching mechanism in order to minimize the database access for readings
  • I'm aware that a caching mechanism can be also used when adopting a CQRS architecture (they solve different problems). I highlighted the usage of a cache in the single database architecture because I think that in such a scenario it is mandatory in order to cope with the locking and concurrency over the single database used for both writes and reads
  • In this question I intend CQRS as an architectural approach for a whole bounded context (as explained here) and not as a design principle as originally presented by Bertrand Meyer (see here)
  • We thought of CQRS as a possible architecture because it is a good choice when you want to independently scale the write and the read sides and you want to have denormalized data ready to be read with a single query (so that reads are fast and efficient). At the same time we have already experienced the complexity that CQRS leads and we are not sure that in this domain it is worth the effort
  • The goal of my question is getting some tips and feedbacks (based on real world projects) from more experienced people related to the problem of being able to scale efficiently on the read side of things

In the case of displaying and editing the blog entries, the concept of separating the responsibilities of edit and read make perfect sense:

  • Separate microservices allow each to scale appropriately
  • The only thing that needs to be understood between them is how you are persisting the data

From there you can decide what infrastructure pieces you need between them. From a cost/performance standpoint, you won't get any better than your cloud blob storage. Of course, blob storage doesn't help with searching and querying, but it serves up resources very fast and you don't have to do anything special to make it scale.

If you offload all the rendering to the client (i.e. a Single Page App), then you only need to pass data back and forth. That leaves your search/query capability which might be better served by ElasticSearch or Apache Solr. An asynchronous task can handle updating your search engine, further decoupling things.

That said, with building for internet scale, you want to minimize the points of contention and share nothing if at all possible. If after all that you feel you still need the Redis cache, you will be better armed to understand where it needs to be included.

Similar Bottom line:

  • Think about the architecture and how you can solve your problems there first.
  • Eliminate sharing if you can.
  • There are no simple answers. I don't know enough about you are doing and why you've chosen the tech stack you have to be any more meaningful in my answer.

I think that CQRS and Redis are solving 2 different problems and are not necessarily mutually exclusive concepts. The main issue that limits scalability is when you have to share things, so minimizing the contention around sharing fixes that. I'm not going to tell you that CQRS is right or wrong for your application, only that you are comparing apples and trucks. They are very different things.

Command Query Responsibility Segregation (CQRS) Has its uses, and works well in focused applications. However, it's a design pattern for your code.

Your other proposal was an architectural decision. While architecture has a big impact on what design patterns are available or relevant to use, the decision is orthogonal to design.

Bottom Line

  • Know your design parameters (do you need to support internet scale?)
  • Understand your bottlenecks (what's preventing you from meeting your goals?)
  • Understand the cost of your decisions (how much does it cost to run, and how much does it cost to switch?)

Your team needs to agree on the problems that need to be solved, and when you are considering alternatives, make the alternatives of equal type. For example, should we use Redis, ElasticSearch, or just simple cloud blob storage? Those are examples of equivalent architectural decisions, which are themselves not mutually exclusive.

  • I added some comments above because I suspect that my question needs to be clarified a bit. – Enrico Massone Dec 7 at 8:56
  • What do you exactly mean by point of contention and sharing ? Can you please provide an example ? – Enrico Massone Dec 7 at 17:27
  • The primary point of contention is when there is the concept of locking. Most SQL databases with ACID transactions employ locking as an example. A database or even blob storage is a resource shared between the blog writer service and the blog reader service in this scenario. NoSQL databases typically favor eventual consistency which removes the need for locking in the same way that traditional database servers do. Increasing the nodes in a NoSQL cluster also reduces the likelihood of contention. – Berin Loritsch Dec 7 at 19:14
  • Does the combo low number of writes (due to the particular domain being modeled) plus read as many data as possible from an in memory cache (due to a precise design choice) could help in reducing the lock contention in the database ? I think it does because you have a low number of writes and a low number of reads (all cachable data will be read from the cache, no access to the database) – Enrico Massone Dec 10 at 9:00

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.