If you have been in the software industry for some time you would have heard things like. Yes we do Scrum but …we do not have timeboxed sprints. Yes, we write automated user acceptance tests but … as a part of the Sprint planning we decide whether we would write automated test for a story or do ad-hoc manual testing. Oh, and our test coverage is around 20% (consider this on a good day ;). Likewise, there is a common theme now a days of everyone doing microservices. Hah ! We are interviewing quite a few people at Knoldus right now and as you would have guessed, everyone is building their platforms with microservices.
Let us see some of the common issues that people have (and these are the most common ones that we hear). I really like the first one
- All of them run in the same JVM – Yikes, Wow, Are you kidding ! This is something that we hear a lot. I created a many microservices and then we bundle them together and run them as one BBOM (Big ball of Mud). What do you mean by a microservice? Well, we have a different package structure for every service! Nice!
- Shared Database – This is another very common one. We can deploy them separately but they call talk the same database. Well, this is not as bad as the first one but it creates more coupling between the services as they have a common dependency. Any changes on the schema, affect almost all of them.
- Location dependent – The IP of the services is pretty much stagnant and does not depend on service discovery thus leading to brittle services.
- Dependent on each other – One service fails and the other one cannot function.
- Having a shared domain model – Another ignorance like point # 2. For a monolithic scenario such a case works but for microservices, it increases coupling and it an in many cases, it is an indication that the same logical service is getting split across deployable units.
- Any one can call the microservice – there are no well defined boundaries and abstractions and just anyone in the world has access to the API. If you are lucky then at least there is authentication and authorization provided.
- Synchronous calls – The calls between services and the ones with the external world, are all synchronous. Bam! there goes the performance and scalability.
Ok, now let us look at what we really should be doing. Microservices as an architectural concept has given us the first real advantage of building software systems with the best of the breed patterns and designs. Let us see how.
- Since the services need to be independent, they are independently deployed, independently monitored and scaled. It gives us an opportunity to define them with well-defined boundaries. This gets us into the territory of Domain Driven Design. The movement started way back with Eric Evans book but with microservices, now it is a requirement.
- Second thing is that we need to be non-blocking, responsive and resilient. For all of these, we need to be asynchronous. All our communication from the front end to the database interaction has to be asynchronous. If you are in the Scala space, it is easy to pull it off, consider Angular, React -> Play Framework-> Akka services-> Slick /Reactive Mongo-> RDBMS/NoSQL. You get the idea right?
- There needs to be an API Gateway. For smaller deployments, you might be able to get away with that but this is a requirement. The external world needs to talk to the services through the gateway which is responsible for aggregation, acts like a facade, does protocol management and acts as an adapter etc
- There needs to be a discovery mechanism for the service. Either all the services register with a central point and when they need any service they get the address from there or there is a cluster manager which is responsible for the same. There is no way in which we can hard code the service end points. (courtesy – http://theburningmonk.com/)
- Inter service communication needs to be asynchronous. It could be based on messaging formats such as pub/sub, rest endpoints or streams but it needs to be non-blocking.
- Once service cannot die just because another service is not working. In distributed systems, the death of a server or a service can result in death by domino’s. This cannot happen.The solution here is to have exponential back-off and implement the circuit breaker pattern. We can set up the Circuit Breakers (CB) to tell that if there are > 10 failures in a min then we would like to trip it and open the CB. Internally we throw CBException we cannot just be dependent on the TimeOutExpcetion which is the #1 killer of distributed systems.
- CQRS and Event Sourcing – Helps in system being modeled in the most natural way of events and interactions. The events are persisted to a Journal and all transactions are aggregated to get to the current state. Snapshots are required so that we do not have to build years of events every time. Commands and Queries are separated so that the interactions are going to different models.
- There are no ACID transactions and no 2 phase commits because all of these are slow and blocking in most cases. In services case, the best way is to be Asynchronous and use compensatory events to handle failures than to prevent failures. The use of SAGA pattern allows to do things in parallel and pass on compensatory events to the transactions which would be applied in case of failures. Sagas come out of the realization that particularly long-lived transactions (originally even just inside databases), but also far distributed transactions across location and/or trust boundaries can’t easily be handled using the classic ACID model with 2-Phase commit and holding locks for the duration of the work.
As you would notice, doing microservices the right way includes a lot of well defined patterns and strategies. Next time when you think about microservices, it would be kind of imperative to think about Domain Driven Design, Asynchronous Communication, Services exposed with API Gateway and discovery of services, Circuit breaker, CQRS event sourcing and Sagas. And, yes they would not all run in the same JVM 😉
A lot of this discussion is already preached by our partner Lightbend, in their webinars. You would also do good to get an early subscription to the book our Knolder Prajut Gogoi is writing on Microservices with Scala.