Why your project may not be ready for microservices
If you believe what you read, 2014 was the year of microservices. However, while there were plenty of projects that implemented microservice architectures, also known as small services, not all of them were successful.
Why not? Some experts suggest that the unsuccessful microservices projects may have skipped the step of building the monolithic version first. Others suggest that using a microservice on a small project simply adds overhead without enough benefit.
For the uninitiated, James Lewis and Martin Fowler describe microservices as an architectural style. It is, they say,
...an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.
Microservices or monoliths?
Contrast that with monolithic applications, such as the typical server layer of an enterprise application, the other layers of which are the web UI and database. Often, monolithic applications start out just fine. Over the course of numerous revisions, expansions, and modifications, however, the build time for the monolith can expand, and the intertwined services can become hard to disentangle and debug.
"What about making the application more modular?" you might ask. That will help with maintenance, and possibly reduce build time, but it won't automatically allow the different modules to use different programming languages and data storage technologies. On the other hand, you don't really need different programming languages and data storage technologies. After all, how many technologies do you want to maintain?
An alternative is to use the enterprise service bus approach. Unfortunately, putting the smarts in the pipes leads to unclear boundaries between services. It's better to have smart services and dumb pipes, as Fowler and Lewis argue from experience.
The natural conclusion from Fowler and Lewis' original article is that you should start your design by decomposing your application into small services. But a later argument by Fowler goes on to say that you need the experience of building the monolith first before you can successfully do the decomposition. Therefore, you should only spin off services as the system gets complex and you understand the component boundaries.
Stefan Tilkov offers a counterargument: "Don't start with a monolith...when your goal is a microservices architecture," because "starting to build a new system is exactly the time when you should be thinking about carving it up into pieces." In other words, you should do it right the first time.
In my mind, that raises a more fundamental question that permeates software development: How well do you understand what it is you need to build? Many thinkers over the decades have suggested that nobody ever understands what software needs to do before it has been built, so plans should include the time to build a prototype that you will learn from and then throw away. All too often, however, people argue that there's no time to build the real product, so they maintain the "throwaway" prototype for decades.
The answer to whether you should build a monolith first or decompose first is 'yes.' That is, it depends on how well you understand the problem before you start. If you've done this a hundred times, decompose first. If this area is new to you, start simple and learn as you go.
Think about the pros and cons of microservices. They give you strong module boundaries, independent deployment of lightweight pieces, and the opportunity to use whatever technology is appropriate for each small service. On the other hand, distributed systems inherently have higher latency and opportunities for failure than do monolithic systems, as well as higher operational complexity, so the application has to be big enough to justify the overhead of being distributed.
What does "big enough" mean? It's sometimes useful to argue by reductio ad absurdum. Hello, world doesn't need to be broken down into smaller services. At the other extreme, building a monolithic enterprise resource planning (ERP) system is just asking for trouble: it's too big, and it needs to be decomposed.
Find a balance
Where's the middle ground? It depends. You want fine-grained units of execution that do one thing really well, according to Jason Bloomberg, and he cites one of Sam Newman's rules of thumb for the meaning of "fine-grained, "cited in his book, Building Microservices: Designing Fine-Grained Systems—small enough to be managed by a single team.
Bloomberg also suggests looking at services through parsimony. If a service looks cohesive and deals only with a single concern, then it's probably small enough. If, on the other hand, you look at a service interface and see a number of different concerns being combined, perhaps it's a candidate for further decomposition. At the other end of the spectrum, if the service doesn't do enough to feel useful, perhaps you overdid the decomposition and need to combine it with a related service. It's very much like the game of "find the objects" that people played when designing object-oriented software 25 years ago, but now the objects are services, not classes.
Frankly, when it comes to using microservices well, there's no substitute for experience. If you already have the experience, you can design your microservice architecture right the first time. If you don't, then start with a simple monolith, and spin out services when you've learned their natural boundaries and functions.