The accidental monolith
It is one of those current buzzwords, next to agile, cloud and containers: microservices.
Hyped by Gartner and “mister Service Oriented Architecture” Thomas Erl, this new way of creating business value by building your application as efficiently as possible is to decimate all issues we have had with creating applications in the past decades.
As you would probably have guessed from my slightly sarcastic tone, I’m not quite sure it is the solution to all the wrongs as it is trivially easy to wind up with a very common application type: the monolith.
While working various jobs in the integration realm in both the commercial and public sectors, I’ve seen the tempting promise of the ability to create loosely coupled but (functionally) strongly cohesive enterprise applications being made by a variety of architectural principles, with the previous one being Service Oriented Architecture.
We are taking a little detour here by veering off track and roping in an enterprise architecture during a discussion of a software architecture. Please bear with me on this one, I’ll try to be brief here.
This section is skippable for those who whish to get to the point quickly.
Going off track
Unfortunately, I’ve also seen these principles being trampled under the hooves of the day-to-day business or even the occasional vendor of Enterprise Software () as they completely hijacked the principle and twisted it to fit their own goals.
(I’m looking at you here, Oracle)
SOA is an enterprise architecture instead of an application architecture like microservices, and is promising a lot of good things in the SOA Manifesto by prioritizing business value, strategic goals, extensibility and flexibility over short term gains, custom implementations and (local or early) optimization. To be fair: this is awesome! Getting the most out of your resources in terms of business value is never not a great thing to strive for and these principles in the SOA Manifesto are, in my opinion, excellent tools to actually do so.
However, these principles also prove to be really hard to adhere to as not focussing on short term gains also means that, for a very long time, you have nothing to show for all investments being made in this new architectural principle and new application. Often, this ‘long time’ is too long for an organisation due to either financial or political pressure (or both!) and they start cutting corners by focussing on getting things done sooner instead of getting them done correctly.
Also, the SOA Manifesto was written in 2009 and last updated somewhere in 2013 which means it is *really old* and generally considered to be outdated. Personally, I’m convinced the form (the manifesto itself) might be outdated, but the premise of the manifesto is still highly valuable and applies to many fields in architecture and software development. Especially the principles focussing on flexibility, business value and evolutionary refinement.
As Service Oriented Architecture (among the tech visionaries) goes the same route as our fax machines have (the use of them has become increasingly scarce and is generally frowned upon), a new paradigm has popped up: microservices.
This time another grand master of software development has stepped up to do the marketing for this method: Martin Fowler.
The Microservices Architecture uses the concept of Bounded Context: divide an application into functionally complete and separable subsections which can have their own lifecycles.
Martin Fowler has the following to say about microservices:
The term “Microservice Architecture” has sprung up over the last few years to describe a particular way of designing software applications as suites of independently deployable services. While there is no precise definition of this architectural style, there are certain common characteristics around organization, business capability, automated deployment, intelligence in the endpoints, and decentralized control of languages and data.
- There is no formal definition,
- The application consists of independently deployable ‘services’,
- There is decentralised control of languages and data.
This last part catches the essence of the Bounded Context, albeit rather cryptically. In a microservices architecture, the application is ‘split up’ into chunks, where each of the chunks has a distinctive and restricted set of responsibilities and data.
A simple example is an HR-system in a large enterprise: it owns and handles all data and processes of your employees. Nothing more, nothing less.
A Bounded Context is just that: a chunk of your application, which owns the data of a specific subject (personnel, documents, cases, invoices, inventory) and also provides all the services the organisation needs regarding this data (CRUD and probably a lot more). Please note these chunks are not created along technical boundaries but through functional ones. These Bounded Contexts routinely cross into all three tiers of a multitier architecture, and that is completely fine!
Furthermore such a chunk is completely independent from the rest of the application, has a clear and stable interface and even has the added benefit of being the right size, i.e. to not have need shared ownership across multiple teams.
By splitting up your application into independent pieces and connecting them through well documented and stable interfaces, they are easier to maintain as any changes to a single piece have no impact on other pieces of your application apart from the interactions through the interfaces. This should mean less regression issues and also less complexity.
Getting back on track: the problem
Splitting up your application into independent microservices is hard.
Let me rephrase and blockquote that:
Splitting up your application into independent microservices is really hard.
Microservices tend to either be too big (and become cumbersome, over-complicated and bloated) or too small, leading to a heap of problems nobody anticipated in advance.
There even are names for the latter of the issues: nanoservices or picoservices.
An example of an application in an ideal world:
This application consists of 8 well connected components and should not be hard to comprehend as the number of connections is still low.
By having your application split into (high) tens or even hundreds of pieces (instead of a more manageable amount like 5 to 8) it becomes neigh impossible to keep track of all the relations between the nanoservices. When you consider a nanoservice not only has a version of its API but also its very own version, you might end up with a spaghetti-like mesh of wires between the nanoservices, of which nobody has an overview. It needs no explanation that keeping track of this spaghetti will become increasingly difficult.
This also translates into difficulties if you want to release a new business function of the application. This functional change might impact up to 30% of your nanoservices, and suddenly you need to not only modify, but also test and release a large amount of “independent” artefacts at once. All the while making sure your change does not cause regressions somewhere else in the application. Due to the opaque nature of
the spaghetti your application this might even be harder than it sounds.
A possible solution for this is to stop treating individual nanoservies as independent artefacts and to start bundling them. Either into functional shards (which might have been a larger microservice from the beginning) or even into a single artefact which gets promoted from DEV to your PROD eventually.
This sounds like a familiar thing, right? Well, that’s because it is!
By tying together the ‘independent’ microservices and releasing them all together instead of releasing the services separately, a monolith has been created.
I’m sure that was not the intended outcome, but here we are. You have just achieved the ultimate anti pattern in microservices: the accidental monolith.
If only it were that easy, right?
Alas, quickly scrolling down to the header of a blog post which states ‘solution’ will not save you from this pitfall or get you out of your predicament as there simply is no easy fix.
The true solution is getting the bounds of your Bounded Contexts right. Nothing more, and nothing less.
You should have a manageable amount of microservices, where each one is functionally ‘complete’ in its own subject, is independent enough, has a clearly defined interface and is responsible for its own data.
Furthermore, your organization should focus on achieving business value in the long run and not get distracted by merely achieving short term goals and taking shortcuts, or just harvesting low hanging fruit. Having mature software development teams, respecting (and actually having) standards and guidelines, adhering to a versioning scheme (e.g. Semantic Versioning), routinely eliminating and preventing technical debt and trying to continuously improve all that is possible will minimize the chances of an accidental monolith forming. Thereby maximizing the success of the development of the application.