Let us try to understand, what a microservice is, by visiting the Java EE world where we used Weblogic, JBoss etc to deploy our apps? You would understand this portion of the article if you were lucky enough to have worked with those enterprise containers:-)
Traditionally, in enterprise world you had one code base and everything for the project was in that single code repo and usually you would build an .ear file. I personally was part of many projects where we would build a large .ear file which would have multiple war files (web assets, web apps/services etc), jars(dependencies) etc, so all that was needed for the application to come up was in that one ear. We deployed this ear file on a weblogic cluster and clients called it using the load balancer. following pic depicts the architecture:
It is a simple deployment architecture with weblogic and other expensive and fancy servers make deployment a breeze with elegant UI. You can add more instances of the app behind the load balancer. Simple enough:-) So where is the problem? Well, this is all good and works well in early stages of the project. Lets look at the problems that we encountered as projects kept growing.
- Application becomes large and complex to understand. As the code base becomes big, it becomes tough to maintain the application and/or make changes.
- Ramp up time for new team members is more and initially they’d depend more on the other team members application knowledge and that would take some of their capacity as well.
- Single update to a component means complete redeployment.
- Scaling is a big issue as you cannot pick and choose which part you wish to scale?
- Not the most cost effect architecture as you always end up taking at least the minimum resources needed for the whole app to run successfully on a single node.
- Technology stack is pretty much fixed. Adopting new language or framework might not be possible, and even if it possible, it is not easy.
- CI/CD becomes difficult. I hate this one personally: running all the unit tests of all the components just to test one change:-(
So how does microservices help us with all these issues? Let us understand what a microservice is? It is super simple – breaking down of a monolithic application into multiple well defined application (microservices). So, from the above monolithic architecture image, can we extract 4 well defined applications? and thus call ourselves following microservices architecture pattern? No, absolutely not! Here are the principles that you must follow to when you are moving from a monolithic architecture to microservices.
Principles of Microservices
Evolve microservices around a Bounded Context
Bounded context (BC) is a logical boundary of a sub-domain inside a domain. BC helps split the core domain model as experts from one domain model will term entities differently than experts from other domains, our monolithic mindset may compel us to merge these entities but we must accept the richness provided by these domain terms. BC are supposed to be autonomous as they help developers understand what must be cohesive and what must evolve independently. Following image depicts different bounded contexts present in a conference management system link:
So based on the terminology used by experts from different domains you have entities with different shapes but it might be present across all the microservices? For example, USER entity in the following pic is in pricing microservice and also in conference management but their shapes are different, same is the case with SEAT entity:
Embrace culture of Automation
To fully realize the power of microservices, teams need to invest automation, e.g Use tools like consul, etcd for service discovery. Use tools like Puppet, Chef, Ansible for provisioning services. Have CI/CD build processes that can run tests in automated fashion and also figure out compatibility of dependent services before an actual deployment happens.
Hiding implementation details
Hiding implementation details reduces tight coupling between microservices, thus making each service independent. A well defined bounded context is the first thing you want to get right.
This gives teams ability to choose different tech stack and tools to deliver their applications. Each teams ability to chose a tech stack should only reflect in a positive way and this can be achieved by making sure that each team follows engineering standards like peer review, unit/integration tests, a Dockerfile as a deliverable which can produce a container that then can be run on any EC2 instance or in general the standards per their organization.
This is the fundamental reason for microservices architectures widespread adoption. Teams exposing microservices can adopt Consumer Driven Contracts approach as it makes consumers define what they need. Pact is a nice framework that encapsulates microservices request and response structure. CDCs just define how your microservice is going to be consumed they neither define nor test the business logic, this is to be covered with your unit and integration tests. Dockerize your services to make them platform independent and also to make devops life easy.
Making consumers life easy another very important reason for moving to microservices. If you have used CDC then the consumer already knows what to expect as the microservice was build using the CDC they designed, make the consumers life further easier by documenting the apis example use swagger, make the service discovery easy using tools like (etcd, consul).
Isolating failure is one crucial design decision for a resilient microservice. One of the failing service shouldn’t bring down the experience or availability of the entire eco-system. Think of Circuit breaker pattern as it simplifies building and running microservices. A circuit breaker simply protects a service as when it sees a certain failure, and if the failure threshold is breached, the breaker is tripped aka opened and now all the calls are routed to the breaker which intern can be another service, or merely an error message. But, the goal is to protect the service in context. Hysterix is a good framework from NetFlix which utilizes this design pattern.
Use new tools like New Relic, Datadog etc for monitoring your services. Use logging tools like splunk which can index your nodes and look for specific things you want.
Pros and Cons of Microservices
- Small well focused autonomous applications
- Quick turn around time for changes and releases.
- New team members take less time to understand the changes they need to make.
- Language/Framework independence, Fault isolation etc
- Cloud friendly and Scalable
- Makes ecosystem complex (I’d vote this +100 if I could :-)).
- Too many moving parts
- Request and life cycle management is tough
- Complicated architecture