Hey Matt! My company has gone all-in on microservices. Cool, right?
Wait – you mean your company plans to build all applications with microservices from the get go?
Yes! We’re starting with an ambitious new development project, but we’re already planning to rewrite a lot of our existing applications with microservices too.
The term “microservice” was introduced a few years ago, but it’s really only over the last year that it has taken off. The definition still seems pretty fluid. How would you define a microservice?
It’s this awesome architecture that composes applications out of separate services running on different servers, communicating over HTTP, each backed by their own database. Oh, and the services should be small. Super small. Micro, even.
Why does your company want to build all applications with microservices?
What should we do instead? Keep building monoliths? As if!
Has anyone in your company ever built an application with microservices?
No. And that’s the problem. All we’ve ever built are monoliths. The technical debt is crippling.
Why do your existing applications have so much technical debt?
Because they’re monoliths! All the code is messy and interwoven.
Why didn’t you use the dependency inversion principle to break up your monolith into separate components?
What’s the dependency inversion principle?
It’s a way to decouple your high level policy from your low level detail.
See, in those messy monoliths of yours, I’m willing to bet that your high level policy is coupled directly to your low level detail. The low level details change at a different rate and for different reasons than your high level policy, but when they’re coupled together, they have to change together. This leads to a system that is rigid – a system that’s hard to change.
What’s an example of high level policy and low level detail?
Sure. Imagine you’re building a blog application. One use case of the blog is updating a blog post. If you modeled that use case with an object, the high level policy would probably read something like this: if the user is authenticated and has permission to update the blog post, then update the blog post and save it, then present a success message. If the user isn’t authenticated, tell them to authenticate. If the user doesn’t have permission to edit the blog post, present an error message.
So if that’s the high level policy, what’s the low level detail?
Well, notice I didn’t mention anything about what type of datastore we’re using. It could be a SQL database, but it could be something else too.
Sure, if you like. Or flat files. Or anything that can persist data. See, the “how” of persistence is a detail to the high level policy. It doesn’t care how things are saved. We should be able to plug in any sort of persistence mechanism that we want.
What about the user interface? Is that a detail?
Exactly! I didn’t mention anything about how to present the success message. It could be HTML. It could be a desktop interface. It could be smoke signals. That’s all a detail as far as the high level policy is concerned. We can plug any sort of user interface we want into it.
What else is a detail?
Well, let’s talk about the persistence plug in again. Could we plug in a microservice?
I’m not sure. Maybe?
We could! The high level policy doesn’t care how the persistence plugin works. Maybe it’s a bit of code running in the same process that stores data in a SQL database. Or maybe it’s a bit of code that sends the data off over HTTP to a microservice that stores the data in a SQL database. The high level policy doesn’t care.
So… microservices are details?
Right! A lot of people talk about microservices as an architecture, but they’re not. They’re details. They represent a deployment scheme. Architecture isn’t about details. Architecture is about the choices you make when designing your code that allow you to defer decisions about details until the last responsible moment.
I see. But regardless, I could plug microservices into my high level policy, right?
You could. The question is, should you?
Well why not?
If you turn the persistence plugin into a microservice, you’ll introduce latency into the application. We need to know what we’re getting in return.
I didn’t think about that.
What other tradeoffs would a microservice introduce?
Hmm… well, I guess the microservice would require more code. We’d have to decide on a format for the messages that we send across the wire, and we’d have to encode and decode the messages.
Yep, there’s definitely more code to write and maintain if we turn the persistence plugin into a microservice. What else?
I could imagine having a harder time tracking down bugs in production, since there’s now two running systems to keep track of. I’ll need to make sure I have really good logging in both systems.
Right. If you’ve used dependency inversion to separate high level policy from low detail, you can make those details microservices if you want to. But the decision shouldn’t be made lightly, since there’s some downsides to microservices.
I’m starting to worry about our decision to rewrite all of our applications with microservices. I’m worried we will make microservices without needing them, and then couple our high level policy directly to the details of the microservice, making it hard for us to change our minds later.
That sounds like a nightmare. Good luck.