Microservices is one of the newer concepts and a variant of a Service Oriented Architecture (SOA). Although SOA has been there for almost two decades while the Microservices came into existence in 2012.
The idea is to have small autonomous services to work together to build a large complex application. The approach focuses on individual business sub-domains and building small services making them easier to maintain, and promotes independently deployable pieces thus ensuring that internal changes in one service do not affect or require the redeployment of other services. In today’s software development landscape, most applications are monolithic and one of the drawbacks of this approach is that business owners get to take a very limited number of decisions in a year (slower response times because of dependencies). For instance, upgrading a product, adding newer functionality that is significant in size, etc within a set of related services requires a concerted effort of all concerned parties to deliver changes in a synchronized manner. Microservices allow you to take more far-reaching business decisions more spontaneously as each microservice works independently and an individual team who is managing that has a good control over the changes. This is also helped by the fact that well-implemented microservices attempt to steer clear of the ‘shared database’ model of development (mentioned later in this post).
The microservices architecture allows each team to decide the technology and infrastructure that works best for them, which may be completely different from other microservices that it interacts with for the very same product. Another aspect of choice is seen when attempting to scale a Monolith application, where you need to scale every component as all components under one product typically run under the same process. This usually reduces flexibility that may require only a small subset of the features to be scaled (eg. performance bottleneck in one piece of a payment processing pipeline). Given such circumstances, scaling a monolithic application as a whole may soon turn into an expensive affair. On the other hand, each set of microservices can (potentially) be scaled independently of the others, thus helping focus resources on areas where the problem truly lies.
I have been reading building microservices by Sam Newman and really liked the 8 keys principles explained by him. In this post, I am attempting to provide a high-level overview of those principles and I highly encourage you to read his book to get a more detailed understanding of these concepts.
The eight key principles are
1. Modeled around business domain
Focus on your business domain and identify individual subdomains and build services. A good way to start would be to follow the principles of Domain-Driven Design.
2. Culture of automation
Infrastructure automation is what smart companies are focusing on today. Provisioning a new machine, operating system and service should be automated.
Automation testing and continuous delivery are critical as well so as to deploy/release your software frequently and reliably.
3. Hide implementation details.
Hide your database, and hide your functionality. Each service should have its own database and if shared information is needed from other services, leverage service endpoints designed for the specific subdomain to extract what is expected. While migrating a monolithic application to a microservice(ish) structure, it is often considered easiest to tease apart application level code while leaving the (shared) underlying database as is. There is often a variety of rationale provided for doing this ranging from a lack of confidence in the success of this migration to potential issues faced for data analysis and report aggregation purposes. However, this shared database continues to serve as a source of coupling between the independent services far greater than the decoupling achieved by spinning off the application level services.
4. Decentralize all the things
Focusing on autonomy (giving people as much freedom as possible to do the job in hand), self-service (do you have to create a ticket to provision a machine or you can do it all by your self), shared governance (making architecture work) and avoiding complex messaging is important.
5. Deploy independently
If you have 4 services and all of them have to be deployed together due to dependency then fix that before you create 5th service. Having one service per host makes life very easy for everyone. Docker is getting a lot of traction for such isolation of operating environments.
In such an environment of independent deployments, consumers drive contracts where when you make a change, the consumer service has expectations about not facing challenges when you deploy changes.
When there are co-existing endpoints, for instance in an upgrade scenario, the consumer service would switch to a newer version while the provider continues supporting the existing version for a limited time period this providing some time for other services to migrate without holding the entire system hostage to its changes.
6. Consumer First
As the creator of an API, it is very important that you make your service easy to consume. The documentation plays an important role here.
7. Isolate Failures
Microservice architecture doesn’t automatically make your systems more stable. To the contrary, it makes the overall system more vulnerable to certain types of network and hardware related issues (more points of failure). You should have ways to isolate failures and look for ways to recover such as failover caching and retry logic.
8. Highly observable
It is very important to know what is happening in your system with so many moving parts. Each service may depend on multiple services and vice versa. There needs to be constant observation and monitoring to ensure the whole integration is smooth.
Microservices often communicate via HTTP/REST (Synchronous) or utilizes Asynchronous protocols like JMS, RabbitMQ etc. It is perfectly fine and acceptable to use Synchronous protocols for public APIs while when dealing with microservices internal communications, you should go with Asynchronous protocols.
In a typical monolith application when you want to fetch the data to show in search functionality, all you have to do is join multiple tables and present it to a user. If you want to achieve the same using microservices, there is going to be a big performance hit as you need to retrieve it from each and every microservices which may not be a good idea. In this situation, I would recommend you to go with Elasticsearch. You can display the high-level data coming from one table. When a user clicks on an individual item, you can always go to all microservices. Moreover, you can run them all in parallel on top of it. This can significantly reduce the pain of performance bottlenecks in homegrown solutions. There are multiple scenarios like this which entice teams and organizations to avoid going with microservices while an easier way out exists. I would be discussing some of the very common challenges and their resolution in details in my upcoming blogs
The microservices architecture gives you enormous benefits when done right. When implementing a microservices architecture, you certainly want to keep your services small. Most of the companies and teams I have come in contact with having a tendency to do so with the backend, primarily for two reasons.
- It is expensive in terms time and money.
- Knowledge gap
Although you are better off going with monolith application while you would not gain most of the benefits as you cant deploy your backend services independently without the front end. In such scenarios, not only is application scaling a challenge, you also need to update the front end whenever an API is deployed with breaking changes.
The idea with a Micro-Frontend is to decompose your application into smaller units based on screens representing domain-specific functionality instead of writing large monolithic front-end application. The front-ends are self-contained and can be deployed independently. SPAs (single page applications) are the best way identified so far to go this route and domain driven architecture helps achieve this to a great extent. You can have backend, frontend, data access layer, and database, everything required for a subdomain in one service. Every piece of the service should be worked by an independent team. Collaboration and communication play an important role here and as long as you adhere to best practices and principles of microservices, you are most likely to get successful and gain maximum out of your product/software.
Benefits of Micro-Frontends
- The individual development team can choose their own technology.
- The development and the deployment are very quick.
- The benefit of microservices can be leveraged in a much better way. The dependency is drastically reduced.
- Helps in continuous deployment.
- The maintenance and support are very easy as the individual team owns a specific area.
- The testing becomes simple as well as for every small change, you don’t have to go and touch the entire application.
- The UX consistency is an important aspect. The user experience may become a challenge if the individual team go with their own direction hence there should be some common medium to ensure UX is not compromised.
- The dependency needs to be managed properly. The collaboration becomes a challenge at a time. The multiple teams working on one product should be aligned and have a common understanding.
You can also read :
Micro-Services, Eventual Consistency and Event sourcing patterns