Microservices and CQRS: Why Business Should Care

Alexey Palatkin
Technology Research and Survey
12 min readJun 2, 2020

This post is for you if you are in charge of developing a software system or product and have to reduce cost and time to market, but you are not that well-versed in the delicate technical points. We’ll show you how to approach architecture planning so that the product is scalable and the money is spent wisely. Also, there will be examples illustrating how CQRS can help in the implementation of client applications and whether microservices that salvation that you were looking for.

What is there in software architecture for business?

The link between software and business context. Business context dictates its needs to software products and systems. If a business needs automated processes, improved customer experience, and assistance in achieving KPIs, software products and systems are here for it. This makes it possible to seamlessly develop, scale, and optimize products.

Cost

Having good software architecture is so profitable that it is unwise to ignore it. It makes it cheaper to develop and maintain a product by using a stock of reusable elements. Moreover, it provides a sound basis for communication and straightforward decision-making further down the line. Owing to faster communication and decision-making, the team is more effective. Should you ignore software architecture, you will face more expensive refinements in the later stages of the development process.

Risk mitigation

Software architecture is also there to identify design risks. It can adapt and change in various ways to reduce the possibility or the disruptive impact of the risk.

Competitive advantage

There is always competition in the market. And the winner is the business that can meet the needs and demands of the market ahead of others. How can software architecture help? It ensures faster time to market, it’s as simple as that.

Adaptable business-model

The environment is highly competitive and changes dynamically. You’ll never know when a time of change comes. Businesses need to be very capable to adapt and transform the business model and introduce changes to the product. Architecture roadmap and product roadmap alignment are necessary to retain strategic adaptability. If there is no connection, the business model is flexible no more.

Technical solutions

Product architecture determines the technical solutions and standards. In turn, code-related issues such as programming language, platform, framework, and tools choices not only determine the level of current and future cost but also determine how fast changes can be introduced to the product. It is crucial in a situation when there are so many users that MVP can’t process such a number, which will call for rapid adjustments.

One extreme or the other

Argue about it or not, software architecture gives the business a lot so it can take off. Sooner rather than later you’ll need to think: when should one start considering it, how detailed it should be, and should one or another style and patterns be used?

We have come across different points of view. In the right corner, opponents of the software architecture building, claiming MVP does not need it, since the goal is to make MVP as quickly as possible. In the left corner, microservices fans, contending microservices should be put from the very beginning, since it grants scalability and flexibility. Truth is somewhere in between, but each extreme sure will be costly. Later in the article, we will look at how to find a middle way.

Big Ball of Mud

A big ball of mud — a chaotically structured system — comes into being due to a lack of architecture design and planning. Such systems develop throughout time as a business stakeholder specifies the requirements for customer service and communication and product improvement.

In iterative and incremental development MVP is developed with no architecture design, sometimes nonfunctional requirements are not even taken into consideration. Then a series of functionality is added to MVP, and this process is repeated over and over. It can result in technical debt and a not properly functioning system, that initiates a never-ending fixing cycle.

Alt text: Chaotic architecture of the Big ball of mud

As we are aware, there are many cases when an architectural focus was lacking. What is the cost? Just extremely large sums to remedy that situation. For example, a product crashing while being incapable of managing overwhelming users inflow. As a result, the team is working for hours and hours trying to plug the “holes in the buckets”. Clients leave business fails. What a sorrowful story.

In other cases, the situation can be so bad that the entire system requires re-coding. It is more time- and effort-consuming than all the preceding development work combined. What is even worse is that the users have to assimilate the change. As a result, users are frustrated and more likely to churn.

What is it about microservices, and why is it not working?

Microservices is quite a popular architecture style. They are set against a monolithic architecture that combines different components into a single program from a single platform.

Microservice architecture is created in a suite of several small and independent modular services. Each service is responsible for one task corresponding to a single business capability. An interface called API (application program interface) is there for services interaction. Each API is created to fulfill a specific business goal. Due to the small size of services and their independence, each task can be executed with the most appropriate languages, frameworks, tools, etc.

Alt text: Microservice architecture is created in small, independent, modular services

What is important

In fact, microservices is so popular that some clines insist on building architecture using it from the very outset. What they have no idea about is that it can cost dearly. Without a vision and wisely designed microservices it is impossible to make them work properly. One can’t just draw up a few independent modules and say that’s microservices. This can result in a chaotic and ill-functioning system.

Of course, there are some points very worth paying the closest attention to starting with mistakes. What was the reason for a mistake to occur? Is it even possible to follow the trace? How to find that one service that is a bottleneck in the work of the entire system? Some infrastructure solutions (for example, Elastic for logging) or custom infrastructure services can settle these preoccupations down, but success is uneven.

There is more to pay attention to. Data integrity mechanisms should be taken care of a priori. Shared database for microservices or exclusive one, choose what fits the best. If overlooked, it leads to data inconsistencies, data loss or desynchronization.

Moreover, it is challenging to segregate the system into independent services in the early stages of the design process. There is no clear subject field and functionality. If segregated inappropriately, the system will require new work scenarios, hence increasing the cost. Worse still, the system can’t function properly (or don’t function at all).

The price is high

The costs of deploying infrastructure for several services in several configurations, the cost of carrying out performance and scalability tests is really high even for a small system. But if the project is that small, is microservices even worth it? These expenses can be easily avoided.

Furthermore, the outcome is always unknown. Will business take off, will it close down? How many customers are ready to give money for the product? It is dangerous to make large investments in the architecture design at the early stages of product development because it is hard to predict the future.

A well thought out architecture is the foundation of a successful product

A competent decomposition and description of the business capability is crucial in the microservices architecture design. If loosely coupled services are decomposed inadequately, the likelihood of not being able to trace and verify all system behavior scenarios is high. Moreover, it may hinder scheduled and urgent revisions. Failures will appear one after another, creating a snowball. It is avoidable, though, if there is a certain architecture vision, a clear understanding of how the system will work on all the levels, who will be using it and for what purposes.

Example

Say, we build an e-commerce website. What are the things that make the user experience positive? All the necessities: accurate order assembly by the Order Picker at the warehouse, convenient online payment, and delivery options available to the customer. These are based on a thought out the user interface for warehouse staff and integration with payment systems and warehouse logistics system. The importance of the connection between business processes and website functionality is a big deal. If one fails to capture that connection, the system will not be able to correspond to the business requirements. As a result, there is a useless system that can’t be used by either customers nor warehouse staff. And, besides, additional budgeting is required to fix that situation. These are expenses that are not justifiable and could be avoided.

The situation when money has been spent on irrelevant features is common. We may build the needed features at the wrong time and choose the right time for the wrong features. It can be avoided if there is a thorough understanding of how the system services interact with one another.

We can use a CRM system as an example of a situation where it is possible to use a ready-made solution since the customer does not require anything unique in terms of functionality. Decision-making on the software architecture choice, decomposition of the system, etc. is possible only after detailed research on the business domain.

Where should you begin?

Workshop time! A workshop will help you to come up with architectural solutions, low-fidelity system design, and main use scenarios. Be careful dealing with startups. At first, fill out a Business Model Canvas and only then discuss the viability of the business idea with all the stakeholders. Your output will be a document describing non-functional requirements for the system.

One option is that the workshop will show that the business idea is not viable and it will be better to shut down the project. It is not that bad as it may look. You will see the deficiencies and save money, time, and efforts by avoiding technical implementation. But even such an outcome will result in better understanding and trust between the client and the technical team.

Is it even worth it? Definitely. Profits are quite large for only a two-day workshop! It demonstrates the majority of the mistakes in product development, that now you have time to prevent. It is cheaper to prevent, not to correct.

The world we knew so well had changed in a matter of days. The Covid-19 pandemic forces businesses to meet new days in a different environment and adapt to the drastic changes. Adapt and overcome or die, the options are limited.

We mentioned it above and we’ll repeat it: this flexibility to adapt to the environment is based on a thought-out architecture by means of trustworthy practices, such as the CQRS pattern. For instance, using CQRS at the logical level at the beginning of the design process will enable us to physically locate the services as the business and product scale. This, in its turn, makes it possible to reuse the split into independent components functionality. It is worth mentioning that it’s not crucial to have services physically segregated. Even so, do not waste already limited resources on monitoring and implementing distributed transactions and other dependencies irrelevant at this early time.

CQRS at the logical level

CQRS definition

Command Query Responsibility Segregation (CQRS) is a popular architectural pattern that divides reading and writing into two different actions: a command that changes or updates data, and a query that doesn’t change data and only reads it.

Our experience

In this project, our task was to develop a system for a client managing a fleet of sea vessels. The client underlined that the budget was tight and so were the deadlines, so we had to figure out some way to build the system fast and in a cost-efficient manner. So we decided to implement CQRS at the level of logical architecture since it was thrifty and fast.

Alt text: This diagram depicts the approach of implementing CQRS at the level of logical architecture.

It is important to analyze the functionality of the system to make it more effective in regards to further cost-effective development, scalability, and maintenance. We noticed that the Admin Office Desktop App users from the office more often read data from the vessels rather than make any data entries. And vice versa, the users who are on board the vessels in the offline zone more often submit data rather than read. It is important since the architecture is the core of further (cheaper and easier) development, scalability, and maintenance, it makes. If need be, it would be significantly easier to single out query contexts and build services with the DB optimized to enable reads, as well as to single out command contexts into separate services.

Examples of segregation

You can a few examples of the main # C classes below. Feel free to use them in your own projects.

/// <summary>/// Universal factory for creating queries./// To be implemented in Composition Root (web api project, website main prototype, etc)/// </summary>public interface IHandlersFactory{IQueryHandler<TQuery, TResult> CreateQueryHandler<TQuery, TResult>();IAsyncQueryHandler<TQuery, TResult> CreateAsyncQueryHandler<TQuery, TResult>();ICommandHandler<TCommand> CreateCommandHandler<TCommand>();IAsyncCommandHandler<TCommand> CreateAsyncCommandHandler<TCommand>();}/// <summary>/// Basic interface for the command/// </summary>public interface ICommandHandler<TCommand>{void Execute(TCommand command);}/// <summary>/// Basic interface for the query/// </summary>public interface IQueryHandler<TQuery, TResult>{TResult Execute(TQuery query);}/// <summary>/// Factory Ninject to create standardised commands and queries/// </summary>public class NinjectFactory : IHandlersFactory{private readonly IResolutionRoot _resolutionRoot;public NinjectFactory(IResolutionRoot resolutionRoot){_resolutionRoot = resolutionRoot;}public IAsyncCommandHandler<TCommand> CreateAsyncCommandHandler<TCommand>(){return _resolutionRoot.Get<IAsyncCommandHandler<TCommand>>();}public IAsyncQueryHandler<TQuery, TResult> CreateAsyncQueryHandler<TQuery, TResult>(){return _resolutionRoot.Get<IAsyncQueryHandler<TQuery, TResult>>();}public ICommandHandler<TCommand> CreateCommandHandler<TCommand>(){return _resolutionRoot.Get<ICommandHandler<TCommand>>();}public IQueryHandler<TQuery, TResult> CreateQueryHandler<TQuery, TResult>(){return _resolutionRoot.Get<IQueryHandler<TQuery, TResult>>();}}Examples of Binding of queries with Ninjectpublic override void Load(){// queriesBind<IQueryHandler<GetCertificateByIdQuery, Certificate>>().To<GetCertificateByIdQueryHandler>(); Bind<IQueryHandler<GetCertificatesQuery, List<Certificate>>>().To<GetCertificatesQueryHandler>(); Bind<IQueryHandler<GetCertificateByShipQuery, List<Certificate>>>().To<GetCertificateByShipQueryHandler>();………….}

Inject IHandlerFactory into the class and use your commands and queries as it is below.

Query example:

Ship ship = mHandlersFactory.CreateQueryHandler<GetShipByIdQuery, Ship>().Execute(new GetShipByIdQuery(id));

Command example:

mHandlersFactory.CreateCommandHandler<DeleteReportCommand>().Execute(new DeleteReportCommand(report));

By observing the basic principles of SOLID, namely Dependency Inversion and Dependency Injection, and correct Inversion of Control Containers, it is possible to reuse easily manageable elements elsewhere in the system.

3 parts of the system that can work similarly to the Model are detailed in the diagram.

  1. Admin Office Desktop App — Model interaction
  2. Ship Desktop Mechanic App — Model interaction

All architectural decisions must be based on and developed according to the business domain, functional and non-functional requirements by any means necessary. However, there is no need to design the architecture for the entire product or system from the get-go, no need to abandon architecture completely. The following questions will help you with the decision making process.

  1. Why is the structure of the software looking this way? What was the reason for using this particular implementation?
  2. Will a new implementation suit well the overall structure of the system?
  3. How will the non-functional requirements of the entire system be followed?

In the next article, we’ll talk about allocating the Bounded Context in the system. It simplifies getting a good overview of the customer’s business structure, building partnerships with a client, and managing the regression in the product.

Benefits of CQRS

Multiple design patterns and principles were used as a foundation for the application of basic architecture: MVVM, SOLID, CQRS, etc. It gave us the possibility to reuse functionality in separate client applications and that, in turn, made the implementation fast and cost-effective.

The team had already developed classes and the same level of understanding of the application architecture at the beginning of the development process. In subsequent refinements, it had fulfilled the expectations placed in it: about 40% of the functionality was fully reusable. The customer significantly reduced the cost of implementing functions by around 30–40%.

In conclusion

There are still two extremes. The first one is incorrectly adopted Agile when there is no design, there is just “build, release, get the feedback, repeat” seasoned with microservices as the greatest salvation of all times. The second one promotes a costly approach where the architecture takes into account the smallest details and is put from day one of the development processes.

And microservices! They will definitely save us! At the very least, they say it everywhere, it comes from all quarters.

Both extremes result in poor product and wasted cash. A balanced approach is better for both the team and the business.

Firstly, the decision-making process based on a certain vision for the product architecture is much easier. As a result, it makes development cheaper in the later development stages.

Secondly, documenting a software architecture should contain all the adopted architectural decisions to ensure the understanding of why the architecture looks as it does.

Thirdly, always keep an eye out for the product environment. The whole risk picture and decisions to implement the change is necessary for the business to take off.

Thus, future product scaling with a firm understanding of what a product’s architecture will look like alongside competent practices when writing code.

--

--

Alexey Palatkin
Technology Research and Survey

I help Project Managers increase the efficiency of interaction between DevOps teams. CVO — KonigLabs.com