DEV Community

Cover image for Is it Time to go Back to the Monolith?
Shai Almog
Shai Almog

Posted on • Originally published at debugagent.com

Is it Time to go Back to the Monolith?

History repeats itself. Everything old is new again and I’ve been around long enough to see ideas discarded, rediscovered and return triumphantly to overtake the fad. In recent years SQL has made a tremendous comeback from the dead. We love relational databases all over again. I think the Monolith will have its space odyssey moment again. Microservices and serverless are trends pushed by the cloud vendors, designed to sell us more cloud computing resources. Microservices make very little sense financially for most use cases. Yes, they can ramp down. But when they scale up, they pay the costs in dividends. The increased observability costs alone line the pockets of the “big cloud” vendors.

You can check out a video version of this post here:

I recently led a conference panel that covered the subject of microservices vs. monoliths. The consensus in the panel (even with the pro monolith person), was that monoliths don’t scale as well as microservices.

This is probably true for the monstrous monoliths of old that Amazon, eBay et al. replaced. Those were indeed huge code bases in which every modification was painful and their scaling was challenging. But that isn’t a fair comparison. Newer approaches usually beat the old approaches. But what if we build a monolith with newer tooling, would we get better scalability?

What would be the limitations and what does a modern monolith even look like?

Modulith

To get a sense of the latter part you can check out the Spring Modulith project. It’s a modular monolith that lets us build a monolith using dynamic isolated pieces. With this approach we can separate testing, development, documentation and dependencies. This helps with the isolated aspect of microservice development with little of the overhead involved. It removes the overhead of remote calls and the replication of functionality (storage, authentication, etc.).

The Spring Modulith isn’t based on Java platform modularization (Jigsaw). They enforce the separation during testing and in runtime, this is a regular Spring Boot project. It has some additional runtime capabilities for modular observability but it’s mostly an enforcer of “best practices”. This value of this separation goes beyond what we’re normally used to with microservices but also has some tradeoffs. Let’s give an example. A traditional Spring monolith would feature a layered architecture with packages like:

com.debugagent.myapp
com.debugagent.myapp.services
com.debugagent.myapp.db
com.debugagent.myapp.rest
Enter fullscreen mode Exit fullscreen mode

This is valuable since it can help us avoid dependencies between layers. E.g. the DB layer shouldn’t depend on the service layer. We can use modules like that and effectively force the dependency graph in one direction: downwards. But this doesn’t make much sense as we grow. Each layer will fill up with business logic classes and database complexities. With a Modulith, we’d have an architecture that looks more like this:

com.debugagent.myapp.customers
com.debugagent.myapp.customers.services
com.debugagent.myapp.customers.db
com.debugagent.myapp.customers.rest

com.debugagent.myapp.invoicing
com.debugagent.myapp.invoicing.services
com.debugagent.myapp.invoicing.db
com.debugagent.myapp.invoicing.rest

com.debugagent.myapp.hr
com.debugagent.myapp.hr.services
com.debugagent.myapp.hr.db
com.debugagent.myapp.hr.rest
Enter fullscreen mode Exit fullscreen mode

This looks pretty close to a proper microservice architecture. We separated all the pieces based on the business logic. Here the cross dependencies can be better contained and the teams can focus on their own isolated area without stepping on each other's toes. That’s a lot of the value of microservices without the overhead.

We can further enforce the separation deeply and declaratively using annotations. We can define which module uses which and force one-way dependencies. So the human resources module will have no relation to invoicing. Neither would the customers module. We can enforce a one-way relation between customers and invoicing and communicate back using events. Events within a Modulith are trivial, fast and transactional. They decouple dependencies between the modules without the hassle. This is possible to do with microservices but would be hard to enforce. Say invoicing needs to expose an interface to a different module. How do you prevent customers from using that interface?

With modules we can. Yes. A user can change the code and provide access, but this would need to go through code review and that would present its own problems. Notice that with modules we can still rely on common microservice staples such as feature-flags, messaging systems, etc. You can read more about the Spring Modulith in the docs and in Nicolas Fränkels blog.

Every dependency in a module system is mapped out and documented in code. The Spring implementation includes the ability to document everything automatically with handy up-to-date charts. You might think, dependencies are the reason for Terraform. Is that the right place for such “high level” design?

An Infrastructure as Code (IaC) solution like Terraform could still exist for a Modulith deployment. But they would be much simpler. The problem is the division of responsibilities. The complexity of the monolith doesn’t go away with microservices as you can see in the following image (taken from this thread). We just kicked that can of worms down to the DevOps team and made their lives harder. Worse, we didn’t give them the right tools to understand that complexity so they have to manage this from the outside.

That’s why infrastructure costs are rising in our industry, where traditionally prices should trend downwards… When the DevOps team runs into a problem they throw resources at it. This isn’t the right thing to do in all cases.

Other Modules

We can use Standard Java Platform Modules (Jigsaw) to build a Spring Boot application. This has the advantage of breaking down the application and a standard Java syntax. But it might be awkward sometimes. This would probably work best when working with external libraries or splitting some work into common tools.

Another option is the module system in maven. This system lets us break our build into multiple separate projects. This is a very convenient process that saves us from the hassle of enormous projects. Each project is self-contained and easy to work with. It can use its own build process. Then as we build the main project everything becomes a single monolith. In a way, this is what many of us really want…

What about Scale?

We can use most of the microservice scaling tools to scale our monoliths. A great deal of the research related to scaling and clustering was developed with monoliths in mind. It’s a simpler process since there’s only one moving part: the application. We replicate additional instances and observe them. There’s no individual service that’s failing. We have fine grained performance tools and everything works as a single unified release.

I would argue that scaling is simpler than the equivalent microservices. We can use profiling tools and get a reasonable approximation of bottlenecks. Our team can easily (and affordably) set up staging environments to run tests. We have a single view of the entire system and its dependencies. We can test an individual module in isolation and verify performance assumptions.

Tracing and observability tools are wonderful. But they also affect production and sometimes produce noise. When we try to follow through on a scaling bottleneck or a performance issue, they can send us down the wrong rabbit hole.

We can use Kubernetes with monoliths just as effectively as we can use it with Microservices. Image size would be larger but if we use tools like GraalVM, it might not be much larger. With this we can replicate the monolith across regions and provide the same fail-over behavior we have with microservices. Quite a few developers deploy monoliths to Lambdas, I’m not a fan of that approach as it can get very expensive. But it works...

The Bottleneck

But there’s still one point where a monolith hits a scaling wall: the database. Microservices achieve a great deal of scale thanks to the fact that they inherently have multiple separate databases. A monolith typically works with a single data store. That is often the real bottleneck of the application. There are ways to scale a modern DB. Clustering and distributed caching are powerful tools that let us reach levels of performance that would be very difficult to match within a microservice architecture.

There’s also no requirement for a single database within a monolith. It isn’t out of the ordinary to have an SQL database while using Redis for cache. But we can also use a separate database for time series or spatial data. We can use a separate database for performance as well, although in my experience this never happened. The advantages of keeping our data in the same database is tremendous.

The Benefits

The fact that we can complete a transaction without relying on “eventual consistency” is an amazing benefit. When we try to debug and replicate a distributed system, we might have an interim state that’s very hard to replicate locally or even understand fully from reviewing observability data.

The raw performance removes a lot of the network overhead. With properly tuned level 2 caching we can further remove 80-90% of the read IO. This is possible in a microservice but would be much harder to accomplish and probably won’t remove the overhead of the network calls.

As I mentioned before, the complexity of the application doesn’t go away in a microservice architecture. We just moved it to a different place. In my experience so far, this isn’t an improvement. We added many moving pieces into the mix and increased overall complexity. Returning to a smarter and simpler unified architecture makes more sense.

Why use Microservices

The choice of programming language is one of the first indicators of affinity to microservices. The rise of microservices correlates with the rise of Python and JavaScript. These two languages are great for small applications. Not so great for larger ones.

Kubernetes made scaling such deployments relatively easy, thus it added gasoline to the already growing trend. Microservices also have some capability of ramping up and down relatively quickly. This can control costs in a more fine grained way. In that regard microservices were sold to organizations as a way to reduce costs. This isn’t completely without merit. If the previous server deployment required powerful (expensive) servers this argument might hold some water. This might be true for cases where usage is extreme, a sudden very high load followed by no traffic. In these cases, resources might be acquired dynamically (cheaply) from hosted Kubernetes providers.

One of the main selling points for microservices is the logistics aspect. This lets individual agile teams solve small problems without fully understanding the “big picture”. The problem is, it enables a culture where each team does “its own thing”. This is especially problematic during downsizing where code rot sets in. Systems might still work for years but be effectively unmaintainable.

Start with Monolith, Why Leave?

One point of consensus in the panel was that we should always start with a monolith. It’s easier to build and we can break it down later if we choose to go with microservices. But why should we?

The complexities related to individual pieces of software make more sense as individual modules. Not as individual applications. The difference in resource usage and financial waste is tremendous. In this time of cutting down costs, why would people still choose to build microservices instead of a dynamic, modular monolith?

I think we have a lot to learn from both camps. Dogmatism is problematic as is a religious attachment to one approach. Microservices did wonders for Amazon. To be fair their cloud costs are covered…

On the other hand, the internet was built on monoliths. Most of them aren’t modular in any way. Both have techniques that apply universally. I think the right choice is to build a modular monolith with proper authentication infrastructure that we can leverage in the future if we want to switch to microservices.

Oldest comments (56)

Collapse
 
jkettmann profile image
Johannes Kettmann

Great read. I'm curious about the reasons why you say this:

Python and JavaScript. These two languages are great for small applications. Not so great for larger ones.

Collapse
 
codenameone profile image
Shai Almog

They both have great tools to start out with very little code. They both allow for untyped development which works well when starting a new project. But as you hit the 10k or 100k lines of code, the complexities shift.

E.g. You can no longer hold the project in your head and need both order and team discipline. It's harder to search the code for what you need since the code has higher density. Every line does more things and there's more implicit behavior.

While the Java API and ecosystem are huge. The language itself is relatively small and strict. Both of these can be painful for smaller projects. But they are a huge boon when the lines of code rise. E.g. look at the following code:

var z = x + y;
Enter fullscreen mode Exit fullscreen mode

Ignore for a second that I can instantly see the types and everything in the IDE. I can already make several assumptions here that I might not be able to make in other languages:

  • The operation either concats a String or adds two numbers of the same type
  • z will be of the same type as x or y
  • All variables are declared in this method or class. In rare occasion in the base class of which there can be only one

This might not seem like a huge deal when looking at a single line of code and it isn't. But there's a compounding impact that increases as the number of lines increase.

Collapse
 
corentinbettiol profile image
Corentin Bettiol

Python can still be useful for big projects (Django still powers Instagram for example).

The rigor brought by using a framework and some other tools (black for formatting, mypy for typechecking) may be a key.

Thread Thread
 
codenameone profile image
Shai Almog

Sure. It's a great programming language. It's just easier to write many smaller apps with it instead of one monolith.

Collapse
 
chasm profile image
Charles F. Munat • Edited

And this is precisely the reason that no project should ever have 100k lines of code. Staying under 10k would be even better.

The goal in many organizations appears to be to bulk up the code base as much as possible. The maximum amount of code in any given application should be the amount that can be held in one (1) developer's head at once. As soon as you have to start using swap space, you're in trouble.

The solution is micro-apps, each owned by a single developer. Then communication between those apps using the actor model. Distributed apps, essentially, even if only on the same OS. Once your "app" is a set of black boxes, who cares where they run?

This also permits an "ownership culture" where devs own their own code and no one else touches it (though others might review it).

It wouldn't hurt to eliminate also the at least 50% of code and features that are gratuitous – that no one needs or wants, and that not only bulk up the code and make it incomprehensible, but bloat the interface as well, harming UX. And virtually every "enterprise" app is filled to overflowing with this crap, if we're honest.

Frankly, this was the initial promise of OOP, unfortunately utterly abandoned in practice.

Thread Thread
 
codenameone profile image
Shai Almog

If you read the post you know my answer. This didn't solve the problem. This punted the problem to a new location. Business applications are large and complex, conceptually. So now we have to manage 100+ apps. Each built by a different developer that hopefully did a good job. Then we have to manage all the interconnect, scale and deployment.

You didn't remove complexity. You just moved it to a different place which guess what: increases your cloud costs while reducing performance.

Worse. What if you need to fix a vertical feature? A new regulation that comes in that needs to go across the board? Now I need to go to 30 different microservices and hold all of them in my head one by one?

A monolith might have 1M+ lines of code. But I can do the same thing within the IDE without knowing all the code. I can invoke a method without a network interface and without a circuit breaker. The deployment is trivial.

I don't know about the types of projects you deployed. I can tell you that from speaking to companies over the past few years. It seems microservices made things much harder for all of them.

Thread Thread
 
chasm profile image
Charles F. Munat

Actually, I stopped at your clickbait title.

If you read my response, then you know my answer. I wasn't only recommending an actor model approach. I said, clearly (or so I thought) that most enterprise apps are needlessly, gratuitously, overly complex and feature bloated. I said that I thought that we could reduce that code by at least 50%.

I was being nice. The vast majority of apps out there solve no problems, are needed by no one, and simply waste resources and developer time. They exist because our economic system demands that we churn out more and more "stuff" thoughtlessly and pointlessly.

Suggest that we cut the number of lines of code in half industry-wide and the response you'll get is "what about our jobs?" So the need that the code actually fulfills is employing devs and concentrating capital as we burn through the last of our planet's resources.

It is funny how many people who propose solutions and approaches never seem to consider simply building less. It's always about our insatiable need to build more, faster. Yeah, pay no attention to the man behind the curtain.

I said nothing about microservices, so that's a straw man. And your comment about 1M+ lines of code and using the IDE is actually making my argument: the code outside of your micro-app can essentially be viewed as balck boxes. You only need to know what API to call. How is a set of micro-apps any different IDE-wise?

Most absurd is your comment that we have to manage 100+ apps each built by a different developer that "hopefully did a good job". Mixing that code up and having devs working all over the place and stepping on each other's code will somehow make better devs or make them do a better job or will make it easier to spot the devs who aren't doing a good job? Please.

And bounded code blocks is worse than smashing those apps into pieces and then stirring them all together so that devs are working on the same code and are all over the place? You still have the same number of devs and lines of code, but now they are essentially spaghetti. I fail to see how my suggestion makes things worse.

As for cloud costs, you don't even understand my argument and already you are rushing to deployment. I'm talking strategy and you are arguing tactics. "The cloud" is just another panacea: a shibboleth. Assumed to be the answer without really questioning why.

Essentially, you just repeat the same nonsense that enterprise devs spout whenever they are challenged. Do you actually have any new ideas, or are you just adding to the background noise?

As for my experience, I have plenty, but that's the biggest straw man/red herring of all. Ideas either work or they don't – on their own merits. In my "experience", those with the most experience are often the least willing to consider new ideas. Not sure how that's an advantage.

It sounds to me like you don't actually think there is a problem at all. So what was the point of your article again? What, precisely, were you trying to solve?

Thread Thread
 
codenameone profile image
Shai Almog

I understand the fatigue that comes with the repeating trend cycle, I've been in this industry for many decades and get that. However, if you're unwilling to have your opinions challenged then you're just using traffic to my post to shout your opinion. You have your right to that but I don't think that's a good argument.

Had you taken the time to read about the modular monolith you would have learned that you can split a monolith and get most of the benefits of microservices while still retaining the benefits of microservices.

Having worked at banks, telecos and large startups. It's just impossible to write less. In fact we tried moving to microservices and ended up writing a lot more. Every microservice within the environment needed logic that ended up replicated all over. This is inevitable due to the inter-dependencies.

Managing the production environment was a nightmare. Guaranteeing that eventual consistency will be reached isn't an option for a bank... Imagine if a user and his spouse withdraw a sum in two branches at once. Some problems are just inherently big.

Thread Thread
 
chasm profile image
Charles F. Munat • Edited

It's clear that you don't want to engage with your readers. I guess you think the comment section is for people to sing your praises.

I haven't shouted anything, let alone my opinion. That you say that I'm unwilling to have my opinions challenged says more about you than me. I responded to your arguments – the few that actually addressed mine. You create straw men instead.

Case in point: you continue to talk about monolith vs. microservices as if I had come out in favor of either one. But I proposed a third path, one that hasn't yet been tried anywhere I've seen, but which you reject out of hand without offering real argument other than that you've managed to stay in this field for multiples of ten years and you don't like it.

It is pretty clear that your world is black and white: it is either a monolith or a microservice. Nothing else is possible.

And you provide zero evidence or even a good argument to support your position. Take, for example, your comment that with banks, telcos, and large startups it is "just impossible to write less [code]". It is difficult for me to imagine a more ridiculous statement. Do you proofread before you post?

Anyone reading this who spent even one day coding in a bank or telco or large startup must be rolling on the floor laughing. Are you for real? I guess you only worked in banks, telcos. and large startups whose code was perfectly optimized and contained zero tech debt. Oh, please. Name names! Everyone should know about these amazing organizations.

I doubt seriously, though, that that generalizes to your typical bank. In my own personal experience, FWIW, with banks, utilities, academia, small and large businesses, social media, and more I have yet to find a code base that wasn't enormously inefficient, overly complex, and loaded with tech debt (about which the devs bitched ceaselessly).

I'll leave it to anyone with the stomach to read this far to decide for themselves. Maybe others can post examples of zero-waste code bases. But I get it: microservices are too hard for you and you hate them. I'm guessing that you did them wrong. Why not just be honest about it in your next article?

Feel free to misrepresent my comments in yet another snarky reply. I'm bored with this. Unless you have some actual evidence to support your views, I'll look for greener pastures.

Thread Thread
 
codenameone profile image
Shai Almog

You haven't read the article... I literally discussed a 3rd way. That is featured in the cover image of the article (although it didn't fit in the so called "clickbait title" and dev.to has no subtitle concept).

There is no "evidence" to support architecture choice. It's tradeoffs and experience. But if you haven't read the post how the hell do you know what I claimed in it?

Read my bio. I worked in multiple banks, telecos, fortune 100s and startups over the past several decades.

I think we have a short circuit in communication. I don't think I was snarky. I'm amazed you would call my arguments straw-man arguments when you completely ignored the substance of my post.

Collapse
 
lexlohr profile image
Alex Lohr

That's why at least in the JavaScript world, static typing (e.g. using TypeScript, Flow or Hegel) and mono-repos with well-organized smaller packages are prevalent in larger projects.

There's no inherent shortcoming in these languages preventing you from using them for large-scale applications. Also, as Atwood's law states: "Any application that can be written in JavaScript, will eventually be written in JavaScript."

Thread Thread
 
codenameone profile image
Shai Almog

Yep. TypeScript is great and a big improvement over JS. But here the surrounding environment is still a bit limiting. E.g. modularity and isolation aren't as strict as they are in the JVM world. The general enterprise infrastructure is also far more challenging.

If you compare Spring Boot to NodeJS the difference is stark. Spring is far more vast and elaborate, it provides more facilities for isolating and distributing application components. The world on top of Node doesn't have these concepts as far as I know.

Thread Thread
 
lexlohr profile image
Alex Lohr

Spring Boot is a web framework for Java, NodeJS is a platform to run JS on the server. You're comparing apples to oranges here. Also, NodeJS is fully capable of isolation and distribution using threads and sockets - and abstractions like cloudflare workers make them simple to use.

Thread Thread
 
codenameone profile image
Shai Almog

Spring Boot is a HUGE platform in which the web is a small optional part.

Workers are super cool but that's not what I'm talking about. I'm talking about bean scopes, IoC, dependency configurations, etc.

Thread Thread
 
lexlohr profile image
Alex Lohr

Not sure about those "bean scopes", but it looks a lot like seperated contexts, wrapped in decorators for good measure. Inversion of control and dependency configurations we certainly have.

Thread Thread
 
codenameone profile image
Shai Almog

No. It's complete management of state and those proxies include tremendous hidden power of declarative computing. Everything from transactions, isolation, role based security, retries, etc. can be implemented decoratively thanks to those proxies.

Sorry I wasn't clear about the IoC. I meant the breadth and scope of the implementation in Spring. The pointcuts, the context and constant injection. There's a level of details that's fantastic here. An admin can override injected values from over a dozen (if I recall correctly) sources. This is well documented including the priorities.

Notice this isn't a slight against node. Spring is the 800 pound gorilla that took the Java EE features to the next level. I think Node chose to go in the exact opposite direction. Especially due to its asynchronous nature that made a lot of these features impractical.

Thread Thread
 
stojakovic99 profile image
Nikola Stojaković

You’re right. People try to show TypeScript as an ultimate solution for all the issues JavaScript has compared to other solutions when building enterprise apps. While I love TypeScript, I understand it’s shortcomings (or more precisely, shortcomings of JavaScript ecosystem). Also, stating how everything that can be written in JavaScript will eventually be written in JavaScript is a reason to worry.

Collapse
 
mbanda1 profile image
Nixon

Should have been these languages are famous for building loosely coupled system unlike tight ones.

Collapse
 
chawax profile image
Olivier THIERRY

A Node.js framework such as NestJS gives developers a developer experience that is close to the one of Spring.

Collapse
 
efpage profile image
Eckehard

What about the "Object Oriented Approach"? That was - with compiled languages - a valuable tool to build modular software. Today the principles are often misunderstood or forgotten, but in practice this was an approach to bring different worlds together.

If History repeats itself, maybe this is the next hot topic?

Collapse
 
codenameone profile image
Shai Almog

Sorry, I don't follow the comment?

Object oriented is doing great...

Collapse
 
chasm profile image
Charles F. Munat

Short answer: no.

It is not time to go back -- or forward -- to anything.

It is long past time to start using the right tool for the job, rather than searching endlessly for the tech panacea. Let's take an "Ecclesiastical" approach instead. (See Ecclesiastes 3:1-8.)

Collapse
 
codenameone profile image
Shai Almog

Sure. It's always the "right tool for the right job". I get why Microservices worked great for Amazon... But saying that is a bit of a cop out...

There's a trend that's being pushed heavily and branded as "the right thing" by many vendors with vested interests. Yet it mostly enriches them while increasing costs significantly and a vast majority of the population will be better off with "just" improving their monolith. In that situation we need to make a stand and get the facts right.

E.g. the claim that monoliths don't scale doesn't pass the smell test. Yet everyone repeats it over and over and over...

Collapse
 
chasm profile image
Charles F. Munat

And in six months, they'll all be repeating that monoliths are the only way, and that microservices suck. That's called backlash. And it all goes in cycles. Are you just noticing this? It has been the case in tech for half a century.

People in tech always say that they get it: right tool for the right job. But no they don't. They just say it. They say it so that they can dismiss it and then get on with whatever atrocity they are committing.

Big vendors are big vendors because they put becoming big vendors above everything else, including even the survival of our species. Who cares what big vendors say? Maybe if you really want to make a difference, you should be telling people to stop listening to big vendors who absolutely do not have anyone else's best interests at heart. No matter what they say.

Collapse
 
frothandjava profile image
Scot McSweeney-Roberts

Is Modulith just a trendy new name for SOA?

Oddly enough, I've got an old monolith that I want to break up into separate services but fine grained microservices feels like overkill.

Collapse
 
codenameone profile image
Shai Almog

I hope not. I've had enough of ESB, SOA and that whole fruit salad.

Collapse
 
chamacr profile image
ChamaCR

Good article, I do believe microservices have been pushed by cloud providers. Having said that you didn’t mentioned anything about testing and deployment with Monoliths which is a real pain in the a…

Collapse
 
defufna profile image
Ivan Savu

Adoption of microservices architecture is frequently a case of premature optimization.

Collapse
 
mistval profile image
Randall

I wholeheartedly agree with this and have come to similar conclusions after working in complicated microservice architectures.

In my view, the main benefits of microservice architecture have little to do with how they're deployed, and more to do with how they facilitate creating clear and easily enforceable boundaries between modules, and being able to clearly assign ownership of microservices to teams or individual contributors.

But we can do the same thing in a monolith. It just takes more discipline and it has to be built into the architecture.

I have seen a monolith outgrow what one big PostgreSQL replica set was able to handle, and we ended up sharding, which is a fine solution. But it's also possible, like you say, to have each module use a different logical database, and do any necessary cross-module data joining in application code. These logical databases could all live in the same replica set, until they outgrow it, at which point the heaviest ones could be moved to their own cluster, with few, if any changes to application code. I've never tried this, but I'm interested in doing so, as doing it right would reinforce the boundaries between modules, though I'm aware of what's lost in terms of query-ability, enforce-ability of constraints, etc.

I have also worked with the opposite: a "microservice" architecture where all the microservices talked to the same database and queried and inserted into each other's tables. The only thing that wasn't shared was the code itself, which lived in a bunch of different lambda functions. It was... dumb.

I would also add that the "start with a modular monolith - break it up into separate deployment units when you need to - which will probably be never" idea is a key point of "Clean Architecture" by Uncle Bob, great book.

Collapse
 
cheetah100 profile image
Peter Harrison

The main problem with a traditional monolith is that it builds in the data model and business rules and tightly couples them to capabilities such as storage, integrations and messaging. Micro-services have had a similar issue, in that while they separate concerns they do so by segregation of the domain, rather than based on the capabilities. There is another approach, which is to expel the domain from code and store it in the DB or configuration. Build or buy general capabilities, and have each one handle only those specific responsibilities.

dev.to/cheetah100/capability-drive...

Collapse
 
codenameone profile image
Shai Almog

Interesting.

Isn't that back to the n-tier layered approach?

You gave a bit of a simplistic example with 2 nodes. Can you give a more realistic example here?

How does that work with authorization and authentication?

Collapse
 
cheetah100 profile image
Peter Harrison

I'll give an example of a live environment I'm running now. There is a SpringBoot system which exposes data access API. The domain is a runtime artifact, much like a DB schema. This means you can dynamically add additional tables, fields and constraints at runtime. Just like a DB once you add it the API is ready to roll, along with UI elements that allow you to perform CRUD operations.

Security is built in by default. When you create a table you are assigned as the owner, but you can the assign access rights to others, either directly to users or to entire teams. This mechanism uses Spring Security with custom authorizors. Simple security is READ/WRITE/ADMIN, but there are also more advanced options to control what fields a team can see, or limit the records they can see based on some constraint.

You can specify relational constraints, reflecting DB foreign key constraints, and the API will check the constraints before saving. Only thing is that this system can connect to multiple databases at the same time, so tables may not exist in the same database. It is possible to set up constraints across them.

The APIs validate the data, ensuring it is the right type, that key constraints are checked, and that custom business rules are checked.

Business rules run on every modification of data, on an event basis. Business rules can trigger a wide range of plugins to perform specific actions. This includes running Python or Javascript scripts which can be uploaded at runtime, communicating via other systems via a range of plugins, transform data using transformer plugins and so on. At one point we also had the user web user interface designed using runtime tools.

It was designed from the outset to be scalable, and uses messaging to distribute rule execution throughout the cluster.

Now, there are three services participating; the Spring App, the MongoDB Server, and the JMS Queue Server. Each is set up as a cluster for high availability. Each can be scaled up independently. The Spring App is kind of like a roll your own Lambda solution, in that the dynamically specified Python or Javascript run on any node of Spring App cluster. All Spring App nodes service API calls as well as process events.

It is monolithic in the sense that it looks similar in structure to the bad old days, rather than a complex network of micro-services, but it is different because it expels the domain, and in the process the complexity, into runtime configuration stored in the DB.

dev.to/cheetah100/micro-nightmares...

Thread Thread
 
codenameone profile image
Shai Almog

Thanks for the detailed explanation. If I understand correctly this database layer sits on top of something like MongoDB. Right?

Isn't that a replication of what a database engine offers already when combined with a good caching layer?

How does the team division work?

Do you have teams for every "tier" or vertical teams that edit the entire thing?

Thread Thread
 
cheetah100 profile image
Peter Harrison

It sits on MongoDB, but there is an abstraction layer which allows us to use JDBC as well. It can even download an existing schema and generate the model config. We had separate subteams for UI and back end. Databases don't expose data via REST, nor do they implement the flexible granular permissions we need. It also simplifies the process of querying data. We have BAS configuring the actual customer solution. Similar to a low code solution.

Thread Thread
 
codenameone profile image
Shai Almog

Thanks for taking the time to answer. Interesting read.

Collapse
 
fyakubov profile image
Farrukh Yakubov • Edited

Great post. I've seen people to go for microservices for the sake of microcervies. I've seen people staying stubbornly on monolith even when their architecture is failing. The sweet spot is usually in between based on a given problem, if breaking up is required at all.

Collapse
 
alexradzin profile image
Alexander Radzin

I was glad to find you article. Interesting that on February 14 I gave lecture "Monolith vs. Microservices" where I tried to explain similar things. Some people think that Monolyth==spaghetti code while Microservices==good, well designed etc code. I spent some time explaining why this is not always truth.

Collapse
 
lucabotti profile image
Luca Botti

Read your post. Agree that - mostly - transactional heavy workloads implemented with microservices add some complexity. But in any way, benefits of splitting complex monoliths and layered applications in smaller units deployed as microservices overcome any issue.

Recently I have been involved in a strictly layered application - felt like going back at the beginning of 2000 (EJB 1.1, so to say).

Also, looks like you have fallen in some of the traps of Microservices Development - eg, contract first development should be enforced if rest interfaces are used, no upfront decision in Orchestration / Choreography, monitoring etc.

Microservice is complex, and rather demanding to apply correctly, and lot of things are to be planned beforehand.

Collapse
 
ravavyr profile image
Ravavyr

honestly, your last sentence hit on it.

The right choice is to build something "properly".
A monolith CAN work, and microservices CAN work too.
How well they work simply depends on what the developer(s) working on it know how to do. If their skills and experience are sufficient and the buget [time AND money] allow for it, they can build something good and it will last.

Nowadays though, few companies want to invest all of those resources properly so the vast majority of applications built are rushed and full of holes until "legal" comes around and then everyone panics and applies "accessibility" and "security" as a "ok we need to do this now".

And this is why EVERYTHING has already been hacked.

Collapse
 
cschliesser profile image
Charlie Schliesser

Great read.

"When we try to debug and replicate a distributed system, we might have an interim state that’s very hard to replicate locally or even understand fully from reviewing observability data."

Such pain, I know it.

"...the complexity of the application doesn’t go away in a microservice architecture. We just moved it to a different place. In my experience so far, this isn’t an improvement. We added many moving pieces into the mix and increased overall complexity. Returning to a smarter and simpler unified architecture makes more sense."

I refactored a decent-sized system over the past year to a more unified architecture. We've been able to introduce new features faster and safer, and write better documentation as a result. I think it's a happy medium between the single monolithic architectures of yesteryear and the whack-a-mole environment that microservices can be.

Collapse
 
sproket profile image
sproket

With project Loom looming, Java is about to get green/virtual threads. The scaling is going to go through the roof.

Collapse
 
mindplay profile image
Rasmus Schultz

Use microservice patterns only for features where you know the extra investment is necessary for scalability - it's usually a few, isolated features and endpoints.

Use monolithic patterns for things like back-office solutions and admin pages, where you know the number of users won't grow beyond the predictable.

There is no reason to constrain yourself to exclusively monolithic or microservice patterns - apply the design that makes sense for the problem you're trying to solve.

But yes, default to monolithic patterns - it's cheaper and simpler. If your monolith is well designed, it usually isn't too difficult to extract that one isolated feature or endpoint to a microservice, if that becomes necessary.

Collapse
 
codenameone profile image
Shai Almog

I mostly agree but I'm not 100% sold on the scalability advantage of Microservices. I think it's over-hyped without proof.

Collapse
 
mindplay profile image
Rasmus Schultz

Oh, it's definitely over hyped.

But things like NetFlix, Amazon and Twitter run, and run reliably - these could not have been realized with a monolith, I don't think. At least, I don't see how. So that's "proof", at least in the "the proof is in the pudding" sense.

Also, talking about scale, we're really talking about 3 different things:

  1. Scaling in terms of compute: that's the obvious one.
  2. Projects that need to scale in terms of complexity: if you have two extremely complex subsystems, and the integration between those is relatively simple, I think microservices can offer some beneficial separation there.
  3. Businesses that need to scale in terms of team size: if you have to produce an extreme amount of software, you might have to scale the production by letting individual teams own individual subsystems. (and hopefully this coincides with #2, meaning those subsystems have relatively simple integration points - otherwise it's likely to go horribly wrong.)

That said, almost no projects require those levels of scale.

Thread Thread
 
codenameone profile image
Shai Almog

Facebook and twitter used to be monoliths, but as I said, when you reach that size things change. They spend a lot on these microservices and have huge dedicated OPS/SRE teams to run them.

I think if modular monolith was an option when all of these companies picked microservices, they might have picked that option.

The module approach supports scaling the teams easily since these become separate small projects. The compute scaling is the main thing I had a problem with. I think that dollar for dollar, scaling a modular monolith will be much cheaper if only due to reduction in observability costs.

Thread Thread
 
mindplay profile image
Rasmus Schultz

Yeah, it's expensive and complex, I'm not denying that - I think there are very few cases where it's justified, but I don't like to exclude any options. Your compute bill isn't the only factor.