Microservices in my own, hopefully simple terms
Traditionally web applications are big. You write one piece of software that runs on a server and answers requests in form of HTML, XML or JSON. If you want your web application to do something new, you add that functionality to the existing application. Such big systems are called “monolithic” (a monolith is a very big rock).
Monoliths are problematic, because they usually grow in size and complexity over time. This is a problem when developing something in a team. Developers are adding new code to the system and can’t change or re-use the existing code, because there is many dependencies between the code pieces. They are also too afraid of removing old code because it might be used somewhere.
When delivering such code to clients, e.g. by putting it on the internet, we call that “deploying”. Deploying and the usual testing after deployment is difficult, because within a big system there is a lot of things that can break. Finding out what is going wrong and who should fix it, is very difficult and requires people to know the whole thing.
Another disadvantage is the scalability. By that we mean “how can we serve more users at the same time?” A single web server computer can only handle a certain amount of users accessing it in parallel. Upgrading that computer to better hardware makes it serve more users, but you will soon hit the boundaries of what is possible with hardware. This upgrading is called vertical scaling. We could also put our web application on two or more servers, so that we can handle more users. This is called horizontal scaling. Monolithic applications are traditionally made only with vertical scaling in mind.
In order to simplify the workflow with big applications, we can split it into smaller parts. Each part serves one particular purpose. We call that a “(web) service”. These web services are very flexible to use. You can use them from within your existing monolithic application, either in the server part, or in the client part. You can also have web services that use other web services.
The split into single web services allows you to loosely couple your application. This means that as a user of the service you only depend on the service being up, available and working. You no longer need to take care of its dependencies, its compilation, deployment or testing. (There are even ways of decoupling further through events on queues, or on a bus.)
You can give that responsibility (of testing, compiling, deploying) to a different developer or team. You can’t break their web service because you do not access it through the source code. They can even use a different programming language and you could still use their service.
This independence is made possible by deciding on using a common format and common protocols (a protocol is a way of communicating). For web services the most popular formats are JSON and XML. The protocol used the most is HTTP, because it’s simple, well-supported by all existing software and your browser is using it, too.
The word “micro” in “microservices” just emphasises the idea to make these web services as small as possible. If you need a more complex service, it is usually better to create a new service that depends on one or more others. How to slice your application into services will depend on the domain, and on the data each service should hold.
When to use which?
Use Microservices if:
- you expect many people to work on the system in the long run (loosely coupled)
- you need to scale to millions of concurrent users (horizontally scaled)
- you need a highly available system with redundancy (modularised)
Monolithic applications are not old-school or outdated! They have plenty of advantages over Microservices in certain scenarios. Use them when:
- the system is developed by one person or a small team (single code base)
- the system doesn’t need to be super scalable, e.g. it’s only used by hundreds or thousands of people within a company or a limited circle of customers (vertically scaled)
- you need a simple architecture which is easy to understand, maintain, deploy and monitor (simple infrastructure)
Example of a system built with microservices
Let’s say you have an application where users can create virtual post cards.
Here is a diagram showing the essential components of such an application built in a monolithic style:
(The User Interface component is crossing the border of the system, because in most cases we use a browser to render the HTML generated within the application.)
This is how such an application could be realised using microservices architecture:
Note how each component is standing on its own and is only addressed from the UI.
- A microservice card library that offers a list of card templates including their dimensions and intention.
- A microservice thumbnailer that, given a card template name, offers you a small preview of the card.
- A microservice renderer that expects a template and the text to fill in. It then renders a card image and returns that.
How they’re wired:
- The library is using the thumbnailer to return a list of cards including their thumbnails
- When the user has selected one, the browser sends that template with the user input to the renderer and the browser shows the returned image.
With this approach, you end up with three microservices and a shared web GUI. You can give each service to its own developer or team, test it independently, deploy whenever you want, and even exchange for something completely new, all without touching the other services. At the same time you will have to make sure the services are all compatible to each other, which might require API versioning, dynamic service discovery (e.g. an additional, highly available service that connects you to all other services) and other more advanced techniques.
Note that the shared UI is just one approach (although the most common). You could potentially also have one UI per service, or services dedicated to providing different frontends for the multiple backend services. There is also a lot of discussions and disputes about the data stores (think databases, queues etc.) and whether they should be used by more than one service or whether each service should rather own its data. This is where the paradigm is rather loosely defined.