Musings on software development for the cloud
Is it hard? Not if you have the right attitudes. It’s having the right attitudes that’s hard.
― Robert M. Pirsig, Zen and the Art of Motorcycle Maintenance
Let’s build an application
It is interesting to observe how software architecture has evolved over the course of the last decade. Availability of cloud computing has transformed the way modern applications are built. Mobile devices dictate how applications should interface with its users. Even the development process itself is different now - geared towards incremental delivery of value. But how would one design an application that is capable of evolving without a complete rewrite every few years? This is the question that we asked ourselves when we started development of Veset Nimbus few years back. And although there are many ways of achieving the same result, our answer was just a natural continuation of what we were already doing.
Ingredients for a microservice
- Create services with limited, but well-defined functionality
This is simple. Create a service that is designed to do one thing well, for example, sending an e-mail. Now that service becomes a building block that we can use whenever our system needs to send an e-mail.
- Make services communicate with each other via interfaces
It is important that building blocks fit together and become a functioning system. Having well-defined and easy-to-use interfaces is essential. Representational State Transfer (REST) is an architectural style of web services that brings order to the “wild west” of interprocess communications.
- Agree that these inter-service interfaces are immutable
Interface “is forever”. Or, rather, for the lifetime of the services that use the interface. If an interface needs to change - it is a brand new interface.
- Add fault tolerance
Expect inter-service calls to fail. In fact, it is better to adopt the “let it crash” model for fault tolerance. Instead of trying to recover from all possible failure states, service should instead fail and recycle resources for the next request. It is then possible to retry the request on a higher level of abstraction, possibly by routing request to another service entirely. See reactivemanifesto.org for more details on this approach.
- Bring it all together
Use a service registry, like Netflix Eureka, for service discovery, load balancing and failover. This makes programming against a pool of services easier because it allows offloading some of the complexity of distributed system to a proven and well-tested middleware.
Now, if we take a service-oriented approach paradigm and look at the microservice recipe above, it is easy to become confused beyond belief as to how those two are different. Frankly, from the technology perspective, they are not very different. The usage, however, is different. Microservices are usually designed as fine-grained services with a specific API in mind, whereas SOA are geared toward specific business functions.
Of course, as always, there are two sides to every coin. So here are some positive aspects:
- A smaller service makes it easier to be developed and maintained by a single developer. This promotes a sense of ownership and responsible development;
- Each service can be written using different language and tools. This makes it easy to select better tools and frameworks for each service, which promotes data-driven architecture;
- Easier continuous integration and deployment, because change in a single service does not affect other services in the system;
- Faster adoption of newer tools and frameworks as a result of shorter development cycles and easier deployment;
- Failures are isolated to one service/node/cluster etc which means that recovery is faster and can be invisible to the end-user.
And then there are some drawbacks:
- Additional complexity of the resulting system;
- Integration testing across different environments can become more difficult;
- Information technology operations become an integral part of the development process;
- The architecture of every service should account for possible concurrency issues.