Hacker News new | ask | show | jobs
by Chyzwar 1943 days ago
There is one approach Fowler suggested is SacrificialArchitecture. You build your monolith quickly, get to market fit and once you understand service boundaries you move to microservices.

Personally I would like to try Umbrella Projects[1]. You can design it as microservices but deploy and build as monolith. Overhead is lower, and it is easier to figure out right services when in one codebase. It can be easy implemented in other lang/frameworks as well.

[1] https://elixirschool.com/en/lessons/advanced/umbrella-projec...

7 comments

This! Service boundaries are vital and almost intractable to design up front as you won’t be sure of your systems’ use cases. I’ve worked on numerous micro services systems and all of them were designed in such a way that data loss and unknown error states were mandatory. Micro services seem simpler, but are actually harder than my methodology “as few services as necessary” + bounded contexts within them. Using network partitions to stop spaghetti code just leads to spaghetti network partitions. The discipline to keep this simple and self contained is the important part.
This is why Domain Driven Design should be mandatory reading for all anyone making decisions on Software Architecture.
Unfortunately in actual practise I've encountered significant cargo culting about DDD. Attracts a lot of people with mid level experience who suddenly want to dictate architecture based on theoretical ideals rather than practicalities.

There's no substitute for experience, and specifics of adapting architecture to the context of the problem you're trying to solve.

In one case I wanted to use a technology that actually matches DDD very significantly - but the cargo cultish closedmindedness of the practitioners meant they couldn't even understand how an old idea/tech they hadn't liked or approved was, was actually an implementation of DDD.

The problem there is not DDD, the problem is the people who get closed minded and stuck to their one true way (often without really broad experience to make that judgement call effectively). I've learned that pattern of language of absolutes such as 'should be, mandatory, all' do XXX is often a sign of that kind of cargo cultish thinking.

That's very true, and a problem I've also seen. There are an army of mediocre people who've gone full Cargo Cult with it. Usually comes with a big dose of Uncle Bob: The Bad Parts.
We have exactly the same construct in .Net: a solution file which can contain multiple projects. All of the code is in one place, so you're working with a monolith^. Services are separate and dependencies are checked by the compiler/toolchain - so you design as microservices. Finally deployment is up to you - you can deploy as Microservices, or as one big Monolith.

^ Compile times are fast so this isn't an issue like it can be in other languages.

I like the idea of SacrificialArchitecture. The big downside is to really communicate to management/other departments that it is meant to be a kind of prototype. If it looks good enough and gets paying customers it is hard to find the time to stop, take a step back and re-write
"Nothing is more permanent than a temporary solution"
That's what my company has done. We have a microservice-like architecture, but discourage dependencies on other services. If we need to use one service in another, we use dependency injection (which allows us to switch out the communication protocol later if need be) or talk over a message bus instead. The idea was that we would figure out later which sub-services actually need to be split out based on usage.

AFAIK, we haven't actually split out anything yet after all these years. All of our scale issues have been related to rather lackadaisical DB design within services (such as too much data and using a single table for far too many different purposes), something an arbitrary HTTP barrier erected between services would not have helped with at all.

Does each microservice/module have its own db?
Nope. Everything lives in one big DB, but services are not allowed to touch each others tables directly - communication must be done through service API endpoints.
I have seen micro-service architectures like that. It seems pretty pointless. You should be letting the database do as much of the heavy lifting as you can.
This is what I do in several of my backend Kotlin code bases and it's worked very well. I've also heard it called "distributed monorepo". There are a handful of ways to share code, and the tradeoffs of this approach are very manageable.

The biggest downside I've encountered is that you need to figure out the deployment abstraction and then figure out how that impacts CI/CD. You'll probably do some form of templating YAML, and things like canaries and hotfixes can be a bit trickier than normal.

I've been messing with this using moleculer and node on a project. Feels like a good compromise.
> It can be easy implemented in other lang/frameworks as well.

No it can't, it relies on the runtime being capable of fairly strong isolation between parts of the same project, something that is a famous strength of Erlang. If you try to do the same thing in a language like Python or Ruby with monkeypatching and surprise globals, you'll get into a lot of trouble.

Weird, we do it with Python all the time. Just don't use globals/threadlocals everywhere and you will be good.
As long as you're not using any libraries/frameworks that use them.
I don't think we've ended up with libs that share state in a dangerous way, maybe we got lucky.
Modules are global singletons, class definitions are global singletons. Monkeypatching is less common in Python than Ruby but I'd still consider it a significant risk.