Microservices: Balancing Flexibility and Complexity in Your Project
22. June 2017
“Should I use Microservices?” The decision essentially comes down to the resources your project has at hand. A project’s resources fall roughly into two general categories: available computing capacity and available technical know-how. Microservices can – if used correctly – offer a wide range of flexibility in deciding how best utilise both.
Leveraging Technical Know-How
Microservices encapsulate single deployable units of code. They encapsulate all the dependencies they need to run. Generally speaking, developers have a wide range of choices in deciding how they will code a web service, push notification, asynchronous service, data source, etc. While it is possible to use languages like Python (Flask) or C++ to write a “hello world” web service in two or three lines of code; Java based frameworks, notably Spring Boot have taken the forefront, allowing developers to leverage a wide range of programming languages and effective frameworks. While other Java frameworks like Play (supporting Java and Scala) and Dropwizard can allow developers moderate flexibility in tech stack choices, Spring Boot has grabbed the brass ring, offering a Java container that can encapsulate Ruby, Groovy, Kotlin, Node.js with help from build systems Grails or Maven. This, in addition to Spring’s D.I container along with its countless frameworks are key plus points.
So microservices can allow developers to choose their own programming language. But if your service can read and write REST or generically connect to a common data source, why should you be concerned with the language used to make it run? While microservices offer flexibility, they can greatly increase complexity!
Increased complexity means more points of failure. Narrowing the tech stack can not only help reduce complexity but can promote easier ramping up for new staff, as well lessening the chance of falling into a maintenance trap. Balancing developer choice versus a refined tech stack is one of the key architectural decisions you’ll need to make early on in your project.
Deployment: Leveraging your Available Computing Resources
Microservices can unchain architects and developers from traditional “monolithic” web servers and web containers. Such services, notably Java based ones, use embedded containers and servers (as dependencies) inside a deployed jar. This means there is no explicit need for a large monolith Java container and deployment of components is greatly simplified.
If one abstracts microservice deployment, it can be said to contain 4 key entities: i) The Service itself, ii) the host (i.e. a bare metal machine, rack) iii) a VM (Virtual Machine) and a container (virtual capacity encapsulation technologies, most commonly Docker).
Cost and / or the available resources are the key in deciding what setup best suits the project’s needs.
Additionally, while micro containers help shed the weight of monolithic web containers and servers, they also decentralise some aspects of network communication. This means that networking decisions need to be addressed early on, (which is not necessarily a bad thing).
While this might be a trade-off of sorts, modern virtualization frameworks also integrate some of the deployment complexity into each individual component (Docker, AWS, countless others). This can add extra development time to a component up front and some extra effort during initial planning and architectural project phases.
Managing Complexity: Benefits to the Project, Orchestration
Microservices if used wisely – can divide complexity amongst a team of developers. This division of labour can allow a “one developer to one service” relationship. This division of work can help isolate complexity during development time, at the same time allowing developers autonomy in their development choices.
Of course, it’s great that the project has been broken down into individual components, however what happens when these pieces need to be put back together?
Orchestration of services in a decentralised network is admittedly one the most complex aspects of modern service development. There are orchestration frameworks out now (and on the horizon) that are attempting to abstract out the orchestration process, such as Kubernetes and Docker Swarm.
And while the modular approach of microservices can raise complexity, it can also in the same breath, ease development and testing efforts. A helpful tactic for tackling complex systems is to build realistic dummy services which can help break “chicken / egg” development situations. Examples of this might be dependencies on external services which might only be made available late in a project’s life-cycle or particularly complex components that could bottleneck resources. In addition to isolating points of complexity, micro service architecture by its very nature can help more easily isolate points of failure in a running system.
As for testing, the benefits of container based deployment allows developers to run realistic end-to-end tests with greater ease.
One final though important aspect of orchestration is the the need for service discovery, i.e. individual micro services being assigned I.P. addresses inside containers or virtual machines in which those addresses can and will change. Frameworks like NGINX in concert with infrastructure tools like Consul (which provides a DNS service) can greatly assist in assuring your services communicate with one another and have correct routing (in and out) towards the internet.
Planning and a sound architecture are key in implementing microservice based architecture. Developers can have greater autonomy in tech stack decisions, however a heterogenous tech stack could potentially add complexity. Orchestration of key components amongst a network in which the project owner may or may not have control is one of the more challenging aspects of micro service architecture. When moving away from monolithic applications, network communication and design are also of key importance.
About Justin Theiss
Justin Theiss works at Neofonie Mobile as a Senior Backend Java Developer. Born on the east coast of the U.S. (New Jersey), he’s resided and worked in Berlin for the last 11 years. In his spare time, he is an avid record collector and amatuer musicologist.