Sam Newman had a great presentation currently listed on Vimeo about “Practical considerations for Microservices Architectures”. This is from the 2014 edition of JavaZone conference in Oslo. I found the talk valuable for first-movers who are planning or in the middle of building / transitioning to Microservices based architecture.
I wanted to put together the important points covered in the talk as a checklist for myself when building Microservices, and hopefully this is useful for others as well.
A Summary of all the important points covered in the talk :-
- To understand what composes a Microservices, its important to know about Bounded Context as a way to define the Service Boundary. Eric Evans has a fantastic coverage on this in his book : “Domain Driven Design”.
- Microservices has more to do than just technology and new architecture style. It has to do with how organizations and teams are formed. A knowledge of Conway’s Law, and its implications is useful to understand the “people” part of this paradigm shift.
- The main goal for Microservices is to improve the speed of innovation by allowing heterogeneous technology choices, and building agility.
- Its important to standardize the gaps within the services, instead of worrying what goes inside a service. Common things to standardize between the services :-
- Interfaces – Restful over JSON for example.
- Monitoring – Application, Infrastructure
- Deployment and Testing
- Safety practices – making sure a service does not fail others in a system
- If everybody “owns” a service, then “nobody” owns the service. Make independent teams that are accountable to services. Having shared responsibility leads to reduced accountability. Assign a set of services to a team, and let them own it, and let them be responsible for decisions to build, operate and maintain.
- For shared services, it would be advisable to rotate the ownership to teams like a “rotating custodian”. Hence, these kind of services should have clear custodian model.
- Strong coupling as always is bad. Its advisable to avoid shared databases, serialization protocols to communicate across services. Instead use lightweight open protocols like REST which are resource oriented.
- A good tip to break down existing functionality into services : “Separate Databases before separating services”. This is a good thumb role, when isolating services, and a good model when breaking down monolithic systems.
- When thinking about designing the service behaviour and interface definition, a good advise is to adapt “Consumer” first approach – This means to think through the various types of consumers who would use the service, and what could they use it for. Planning for a API Documentation and Developer Test Console is also greatly advised. Tools like Swagger are useful in this context.
- Monitoring is an essential need, not just a last-minute thing in Microservices world. Investing in Monitoring across all layers and different constituents of a Microservices architecture is as important as developing services in the first place. A good start is to think how different monitoring information could be collected, aggregated and visualized. Sam recommends Logstash, Kibana stack from the Open Source world for log monitoring and analysis. Yammer metrics or Netflix’s Servo is a good pick if interested in Program counters. Graphite with statsd also stood out as good picks to be used when thinking about monitoring.
- Synthetic transactions or Semantic monitoring is an interesting way to check health status of a Production system. This could mean for example: having tests that create an order, cancel and return an order to check if everything is working fine or not periodically in an e-commerce system. We need to be careful in picking up these end to end tests to ensure we don’t do any thing destructive while verifying the end to end flow from time to time. One other tool which could be useful here is Mountebank, that allows quick development and testing for service stubs, useful when building service that needs to be tested in isolation.
- Using correlation id that is generated and passed along all upstream and downstream systems via logs is a good step towards helping debugging issues. Together with metrics in the log via Yammer Metrics, its a potent combination for devising call graphs when diagnosing issues with Service issues and latencies.
- Allowing teams to build their services independently needs a standard way of deployment to ease fast production rollouts. Therefore, one of the recommendations is to evaluate toolsets that abstract underlying deployment differences like Packer from HashiCorp. Container based deployment is also a common place using technologies like Docker.
- Independent teams building micro services frequently run into problems wherein they need to test a service without breaking other dependent service consumers. Change in service should not break Upstream or Downstream systems. Hence, the concept of Consumer Driven Contracts is recommended, wherein service consumers can specify their expectations as “tests”. These tests are run as “unit tests” when building the service. Tools like “Pact” could be used to test Consumer Driven Contracts.
- Reduce the tendency to have a large scope of release. This means to eliminate the need for targeting release of multiple services together, as it breeds coupling. Instead, we would need to try as much as possible to release services independently. The idea is to not let the change build up. Release one at a time, as often as possible.
- Usually in a multi service environment, cascading failures are a big risk. This can lead to conditions where a particular service failures could lead to downtime of the entire system. In most cases, the system should survive outages of service by working in a deprecated mode or with partial failure. There are multiple ways we could use to reduce cascading failures, mostly originating from the book “Release It” by Michael Nygard. This includes patterns like “Bulkaheads”, “Circuit Breakers” and “Timeouts”. Hystrix from Netflix is an interesting implementation of the Circuit Breaker pattern and is widely used.
- In micro services based system, the onus is on to move fast and strong ownership. To allow teams to independently own services and be accountable for the operation requires some sense of discipline and co-ordination. The essential idea is to have these teams to be able to leverage polyglot technology and techniques to deliver services with standard interfaces, monitoring and fail safe mechanisms. A common idea is to built Service template as Boilerplate that are self contained and are a good starter project for any service team. The Service template encapsulates common essentials for any service : Monitoring, Metrics, API tools, Fail Safe like Circuit breakers and deployment among others. This allows independent teams to be able to have the easiest way to do the Right thing – built polyglot services in a standardized non-chaotic manner. Netflix Krayon and DropWizard are good examples of Service templates.
- When starting with micro service orientation, its important to focus on – “How many services are too many ?” instead of “How big or small is the micro service ?”. Its preferable to gradually start with small set of services and ramp up as confidence improves. More number of services at initial stage would lead to the need for managing more moving parts, which may disappoint progress.
I think most of the ideas are very practical and definitely a must in the arsenal for a Microservices practitioner.
The upcoming book by Sam Newman titled “Building Microservices” should also be a great read, based on the content that he already covered in the aforementioned talk.