September 24th, 2019 | by Bartłomiej Undak
Microservice Architecture
Table of contents
What is essential when hunting vampires? Silver, among other things. Silver bullets, to be more specific. Magical artefacts that when equipped, solve any problem. Software development knows vampires very well – creatures of the night, lurking in the shadows, creeping in. Deeply embedded bugs, holes in the documentation, codebase growing into a big ball of mud. Growing application complexity on one hand with cloud infrastructure becoming more and more accessible fostered microservice architecture popularity – but that’s not enough. Microservices architectural style is not a silver bullet to wipe out all vampires, but still very useful. Here are some challenges and solutions.
Microservices are taking the world of software product development by storm. They are even viewed as the only legitimate style nowadays by some. At the same time, other architectural styles like a monolith, microkernel or “classical” SOA are viewed as obsolete. Neither perspective is correct. Every architecture is valid and useful in its own right. Every style fits into a specific set of applications and has its own challenges.
Microservice architecture
Before you can benefit from the advantages of microservice architecture, you need to deal with several challenges:
- The name itself. How micro is the “micro”? Getting service boundaries right is one of the key things to decide, as it’s hard to change later. It is tricky as well. Make your services too granular and your system will suffer from chatty communication between services. If it turns on your “performance” warning light – you are right! On the other hand, pushing too much functionality into single service defeats scalability of the system and could make developers stepping onto one another’s toes. So, how small should be small? The best hint lies in the bounded context concept of Domain-Driven Design. Modell your services around distinct business domains of your system. Expose only the information required by other services and keep your domain model private. Taking e-commerce example, your bounded contexts would be inventory, shopping cart, customer management etc. Now when you structure development teams around services modelled this way, the system architecture would mimic your teams’ structure. And that’s how you could benefit from Conway’s Law.
- Monitoring and health-check. Under microservice architecture, it is easy to forget that with great power comes great responsibility. When an application is broken down into many cooperating and sometimes co-dependent parts, the natural outcome is more ground to cover. When something fails you need a way to find out what and where pretty quickly. This is where health check mechanisms come into play. Monitoring of your infrastructure and each microservice can save you hours by letting you know that something is off. Advanced monitoring algorithms can even do this in advance, so you would be able to react before a disaster happens.
- Testing. When dealing with so many moving parts you don’t want to spend a better part of your day (or night) verifying everything by hand. Embracing a good testing strategy and sticking to it is the key to ensure the quality of every microservice and system as the whole. The good testing strategy should impose coverage of every component and aspect while being reasonable and not to force testing for its own sake. Your toolbox contents are unit, component, integration, contract, end-to-end tests – figure out how to make the best use of that.
- Preparing for failure. In a distributed system question about failure is no longer if but when. It’s like car maintenance – you need to service it regularly and replace parts that are expected to fail soon. A system based on microservice architecture is not as predictable as a car though. You need to be prepared – what to do when any of the components fail. Thinking of it ahead of time and proactively designing your system for failure will make it resilient. Now, whatever comes, there are plans A, B, C etc. When a single microservice misbehaves, others, which depend on it, may just cut it off – that would gracefully degrade system’s functionality but overall, it’s better than bringing the whole system down. That’s what we call failure isolation.
- Dependencies, contracts, and versioning. You should be able to develop, test, deploy and upgrade each microservice independently. Full stop. But how to break dependencies of components which together form a single system? If every microservice is an island, contracts are your bridges. The contract specifies how service’s API looks like, which data it expects, and which one it returns. Think of it as an agreement between supplier and consumer. They agree what is delivered and how but not how goods are produced or who supplies electricity to the factory. Of course, sooner or later you will need to modify your contract. That’s where versioning comes into play. Version your contracts using Semantic Versioning and automate contract’s compatibility tests. Tools like Pact could help you manage all your contracts and their versions.
- Logs. Where are my logs? In monolithic applications that are usually easy to answer and even when woken up at 3 am developer would be able to tell you. Things change radically when you have a multitude of microservices instances and each one produces some logs (we believe we don’t need to convince anyone why logging is so important at all). Usually you need two things: logs consolidation and correlation.
Logs consolidation gives you access to all the logs from a single place. Solutions to that vary, starting from setting up global shared folder to which all microservice instances write, through dedicated microservice which gathers log entries to tools like ELK (Elasticsearch, Logstash & Kibana) + Serilog. That not only makes logging to a common location easy to configure but also makes them easy to search through.
To give your logs more readability add correlation ID to every log entry. Correlation ID allows you to track logical flow as it travels through multiple services. Just remember to pass the ID with every API request you make (HTTP header is perfect for this).
- IT mindset. Adrian Cockcroft, a former Chief Cloud Architect at Netflix, said one time: ‘People try to copy Netflix, but they can only copy what they see. They copy the results not the process’. From everything we have said so far, the following will be rather obvious but still important: adoption of microservices requires in-house skillset or the help of a software product development company. Plus, knowledge and resources – not everyone can pick up the workload and just follow through. You need mindset shift from “throw it over the fence” to “you build it, you run it”.
- Standardization vs. independence. Microservices are largely their own beasts. You want each one to be developed by an independent team and deployed freely when the team decides. Yet you need to keep some baseline. Wise men say you should have a solid infrastructure (the between) and flexible services (the inside). Shared libraries might help, but you need to be careful not to impose rogue dependencies, which could push you into deployment & upgrade coordination hell. Instead, build shared libraries only for the infrastructural and cross-cutting aspects like logging or security – never expose any business domain logic or entities through them. This approach is used in MAKO – a platform for rapid software development.
- Automation. We touched this topic earlier but generally you should try to automate whatever you can – otherwise, you would waste your time for repeatable tasks. From testing to deployment. You need DevOps for that.
- The learning curve for understanding and harnessing potential. According to DZone in their 2018 study on microservices, 59% of respondents admitted using microservices in some capacity. 38% of them use microservices for development and production, 16% for development only, 5% in production only. Almost 60% of asked companies use microservices – that shows it’s not that bad and the architectural style can be mastered. When asked ‘Why are you using microservices?’, 69% responded that making applications easily scalable is imperative, and 64% wanted to enable faster deployments to one part of an app. It would seem that utilizing microservices sounds like a good idea. It is but it’s not so simple. It requires domain knowledge and a highly skilled team to drive the change. There’s a learning curve for pulling the full capability of this architecture.
Microservice architecture – the takeaway
The microservice architecture is very useful. Their scalability is unrivalled, they are easy to recompose and reconfigure, they have native support for cloud, they are highly resilient. But every coin has two sides. You need to be aware of them if you want to do this seriously. If you don’t have in-house competency or you’re short-staffed, look for software product development company to get the best developers for hire.