2

Microservices are Dead — Long Live Miniservices

 2 years ago
source link: https://blog.bitsrc.io/microservices-are-dead-long-live-miniservices-40e4ccf4741
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Microservices are Dead — Long Live Miniservices

Are you really using microservices for your application? Think again.

Photo by Mathieu Stern on Unsplash

Disclaimer alert: this is going to be one of those purists articles that explain how you’re not doing what you think you’re doing simply because you don’t really know the full definition of what you think you’re doing.

If you’re OK with that, then we can go on.

Have you ever defined or implemented a microservices-based architecture? You’re probably wrong about that. I’m sorry, but today I’m playing the role of the “definition police”.

What you’re most likely dealing with are not microservices, but instead: Miniservices. Let’s try to cover why that is and why it’s OK to be wrong about it.

Microservices, Miniservices, they’re all small services, aren’t they?

I mean, yes, you’re not wrong about that, in fact, that’s not where the confusion happens.

We tend to think about “microservices” as small, very logic-focused services that deal with, usually, one responsibility. However, if we look at Martin Fowler’s definition of Microservices — and you know, he’s a very smart guy, so we usually like to know what he thinks — you’ll notice we’re missing a very small, yet key, trait about microservices: decoupled.

Let’s take a closer look at what we’re calling “microservice”. This term gets thrown around so much these days that is getting to a point where it’s exactly like teenage sex: everyone talks about it, nobody really knows how to do it, everyone thinks everyone else is doing it, so everyone claims they are doing it.

Truth be told, from 99% of the interviews I take as a manager, when I ask about microservices I get responses about REST APIs. And no, they’re not necessarily the same thing.

And by definition, REST APIs alone can’t be microservices, even if you split them up into multiple smaller ones, each taking care of a single responsibility. They can’t, because by definition for you to be able to use a REST API directly, you need to know about it.

As a related side-note: There are two types of REST developers (meaning devs creating REST APIs):

  • The ones that implement as many features about REST as they think they need. Meaning that they might care about resource-oriented URLs if they feel like it, and maybe they will worry about their APIs being stateless since it’s not that hard to implement. But this type of developer will 99,9% of the time ditch the HATEOAS (Hypermedia As The Engine of Application State). In other words, self-discoverability of the structure of the API is no longer a feature, the client and the server have a hard-coded contract between the two of them.
  • The ones that follow the REST standard to the letter. I think I’ve only seen one developer like this in my experience — so if you’re like this, please drop a comment and let’s connect! — . Implementing REST APIs this way can take a lot longer, but the result is a lot better, especially because clients have very little coupling with the server, all they need to know about is where it is and what’s the root endpoint. The rest is done through self-discoverability, very cool.

However, in both cases, the coupling between client and server is still there. You can’t get a decoupled communication JUST through REST, and that is why, if we’re strict with the definition of microservice — and we’re trying to be— they can’t be called like that.

So instead, let’s loosen up the belt of definition, leave ourselves some wiggle room and call these services: miniservices.

So we can define a miniservice as:

Small-ish enough services (usually APIs) to be considered “mini”, they still need to take care of only one single responsibility, however, we’re not too worry about the “decoupled with the client” part.

Granted, the less coupled they are the better it’ll be for future changes, having a dynamic client will definitely help adjust faster and without much impact upon changes on the server. That’s definitely a plus, but it’s not part of our definition.

Tip: Build with independent components, for speed and scale

Instead of building monolithic apps, build independent components first and compose them into services and applications. It makes development faster and helps teams build more consistent and scalable applications.

OSS Tools like Bit offer a great developer experience for building independent components and composing applications. Many teams start by building their Design Systems or Micro Frontends, through independent components.
Give it a try →

An independently source-controlled and shared “card” component. On the right => its dependency graph, auto-generated by Bit.

What is a microservice then?

If we want to get technical, by definition we’re still dealing with a service that deals with a single responsibility, but at the same time that it’s capable of being decoupled from whatever client consumes it.

So how can we achieve this decoupling? The trick is to think outside the service: the communication channel.

We tend to assume microservice = REST API and at the same time, REST API tends to automatically get associated with the client-server communication paradigm. That’s it, we went from microservices to client-server in a split second. But let’s rewind a bit.

A while ago, I wrote about my favorite interservice communication patterns, and there I listed one particular option: Asynchronous messaging.

That option does not directly connect the client and our services, instead, it requires a central message bus that takes care of delivering messages back and forth between client and services without them ever having to know about each other. Can you see what we just got there? That’s right, we decoupled the client and the server completely with a simple paradigm change.

Look at the above diagram, the client application starts a potential multi-service request with a single message. It doesn’t even have to know how many services are involved, which is fantastic. That means orchestrating complex request sequences is no longer part of the client’s reponsabilities. That’s a great deal!

Eventually, the client application ends up receiving the result it was looking for. And I say “eventually” here because this is not a synchronous communication model, given how it works, it needs to be asynchronous. That’s not necessarily a problem, it just requires a change in the way we code our clients (and our other microservices as well, considering this is how they can talk with each other too).

Let’s be real for a second: a message-bus based communication model is not great just so that you can correctly call your services “microservices”, it also provides some nice advantages:

  • Your whole architecture, including that of your client applications becomes reactive. This is, in my book, a fantastic advantage, especially if you’re dealing with requests that by nature take a long time to be resolved. Maybe you’re dealing with very complex calculations, running some ML models, or just gathering data from other third-party APIs. Whatever the reason might be, your clients can continue doing something else while they wait for the response, instead of being locked with an active connection hoping it won’t time out. In fact, in this talk I gave recently I cover some of the benefits of a reactive architecture and how you can create one with Redis:
  • Horizontally scaling your services is very easy. With a coupled implementation, such as having REST APIs directly speaking with the client, you’d need to have some kind of load-balancer or API gateway that would allow you to distribute the load between all your copies of the same miniservice. But if we’re dealing with a decoupled architecture, we don’t have to worry about it, the first microservice to receive the message will take care of it, leaving the copies free to take care of the next incoming message.
  • Adding new services has no direct impact on the client. This is brutal if you ask me. Because with a client-server communication, adding a new service (either because you’re adding a new feature or because you’ve decided to split an existing service) means the client now needs to know who to contact for different scenarios. Either that or you moved the orchestration to a central API where all requests land, however, in this case you also have to update that orchestration logic on your service. Granted, the client is not affected, but you still have a side-effect. With a microservice-based architecture, this is no longer the case. The orchestration is done by the message bus, rendering the addition almost impact-free.
  • Easier retry and more resilient architecture. These are two very related points. On one hand you have to worry about what will happen if your services die (or at least are unreachable), for any reason. For client-server communication, that means requests will fail. For a message-bus one however, that just means the request won’t finish until the services are restored. The message bus can store the messages for a while, thus allowing for the creation of a more resilient architecture. At the same time, if you wanted to have some kind of retry logic in case of failure, as I already mentioned that is implied for a microservice-based architecture, while you’d have to code it yourself for a client-server one.

Not convinced yet? Really? Then maybe that’s because your use case is not a perfect match for microservices. Maybe miniservices are the right solution for you.

The main thing to get out of this article, is to understand that while “formally known microservices” might be great, they’re not mandatory for every architecture.

It’s all about trade-offs. Microservices might require a lot more work at an infrastructure level and maybe even ramping up your dev team to properly think about asynchronous communication correctly. However the final product might yield a lot better results and a lot fewer headaches in the long run.

There is no silver bullet architecture that solves everyone’s problems, so don’t take this zealot’s definition of microservices as me telling you to ditch your client-server ways. That couldn’t be further from my intention.

This article is meant to show, once more, that the client-server communication model, while it might be the most common one and easier to implement, is not necessarily the best one.

So what do you think? Are you ready to finally try some microservices for a change? Or will you stick with miniservices for the time being?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK