Get Started Free
‹ Back to courses
Wade Waldron

Wade Waldron

Staff Software Practice Lead

Autonomous Microservices

Overview

When building software, dependencies can be the enemy of robust, scalable code. With microservices, it becomes worse as the network enters into the picture. The problems introduced by these dependencies can be mitigated by building Autonomous Microservices. The goal is to build systems that are loosely coupled and highly cohesive. While microservices may still communicate with each other, they should favor asynchronous communication in order to maintain autonomy. Ideally, a microservice should be able to respond to any requests without having to synchronously communicate with an external system.

Topics:

  • Autonomy
  • Low Coupling
  • High Cohesion
  • Single Responsibility Principle
  • Domain Driven Design

Resources

Use the promo codes MICRO101 & CONFLUENTDEV1 to get $25 of free Confluent Cloud usage and skip credit card entry.

Be the first to get updates and new content

We will only share developer content and updates, including notifications when new content is added. We will never send you sales emails. 🙂 By subscribing, you understand we will process your personal information in accordance with our Privacy Statement.

Autonomous Microservices

Hi, I'm Wade from Confluent.

In this video, we'll explore autonomy in microservices, and why it's valuable.

Before we get technical, let's talk about life at the office, whether remotely or in person.

I like to use people as a metaphor for microservices because, in many ways, we are a natural distributed system.

When we work at the office, there's a good chance we'll be interacting with other people.

Those other people can be considered external dependencies.

We require contributions from them but lack direct control over their actions.

This can lead to delays if they fail to deliver results in a timely fashion.

This is true whether they go on vacation, get sick, or just take longer than expected.

In a perfect world, we'd be able to complete our work without relying on anyone else.

At the same time we don't want to do the entire job ourselves.

Sharing the work with a team is what allows us to scale.

So what does this have to do with microservices?

Microservices are a team of applications working together to complete a task.

These services often depend on each other which creates external dependencies.

Those dependencies introduce delays.

And although a microservice doesn't get sick or go on vacation, it might fail or need to be redeployed.

It can also take longer than usual if it gets busy.

These scenarios result in timeouts and failures in otherwise healthy systems.

The problem with both the office and our microservices is a lack of autonomy.

Autonomy is the ability for a member of the system to act on their own without outside interference.

Each external dependency reduces the autonomy of a microservice.

Imagine an eCommerce site that has information about customers, orders, shipments etc.

The status of an order might include elements from each of these entities.

That means, it might need to pull information from multiple microservices.

However, that creates a lot of coupling between the services.

Each call results in a small delay.

What happens if one of those services is unavailable?

In that case, the task would fail.

But what if we could make the system more loosely coupled instead?

Rather than pulling data from the microservices, each service could push data to a messaging platform such as Kafka.

The service responsible for the order status could consume the messages and build a view of the order ahead of time.

That way, when the status is requested, everything is already available and no external calls are required.

This changes the coupling so each service depends on the messaging platform and the message format, but none of them are directly tied to each other.

It also breaks the temporal coupling.

When the services communicated synchronously, everything had to happen at the same time.

However, with this new architecture, the communication is asynchronous which allows more flexibility for when messages are processed.

Services can go offline without having an immediate impact on each other.

In this example, when the Orders service is offline, it's still possible to obtain shipping or customer data because those two systems are still fully functional.

This improves the autonomy of each microservice and allows them to be more resilient.

When building autonomous services, it's tempting to put more functionality into a single service.

If everything lives in one service, then there are no external dependencies.

However, this creates its own problems.

The order status for our eCommerce platform requires orders, customers, and shipments.

We might decide to roll all of those into a single service.

As long as the system is running, all of the necessary pieces are there.

But, they also share resources.

Imagine there is a new product launch and the orders get overloaded.

Unfortunately, the entire system is sharing processors, memory, and threads, so it's not just the orders that get overloaded.

Suddenly shipments stop responding, even if they are completely unrelated to the new product.

Essentially, problems in one part of the system impact the entire system which can result in cascading failures.

This is the type of scenario we want to avoid.

To do that we build highly cohesive microservices.

Think of cohesion as how closely related individual tasks in the service are.

Adding and removing items from an order are highly cohesive operations because both are operating on the same entity.

But adding an order and adding a customer, are not cohesive because the entity involved is entirely different.

One way to think about cohesion is through the lens of the Single Responsibility Principle defined by Robert C. Martin in 2003.

The principle states, "A class should have only one reason to change."

This definition can be extended to "A microservice should have only one reason to change."

In the case of adding and removing an item, we can say that the reason for the change is that the order is being updated.

Therefore this could fit within the rules of the single responsibility principle.

On the other hand, adding an order and adding a customer update a different entity.

So in that respect, it creates two reasons to change and violates the principle.

Based on this, we can suggest that orders and customers probably don't live in the same microservice.

Domain-driven design is a set of architectural patterns created by Eric Evans in 2003.

Evans introduces the concept of a bounded context.

Large systems can be divided into smaller bounded contexts based on the use of language and business rules.

Bounded contexts make an excellent starting point for dividing up microservices.

They introduce clear boundaries with few connections, making them loosely coupled and highly cohesive.

One of the main benefits of low coupling and high cohesion is how they impact the cost of a change.

Low coupling means that changes in one system shouldn't have an impact on another.

While high cohesion means that related changes are grouped into the same service.

This makes any changes easier because they tend to be more isolated.

This outlines some of the key goals when building autonomous microservices.

Ideally, requests can be answered immediately without requiring external dependencies, and changes can be isolated to just one service.

That doesn't mean that these services don't cooperate or communicate, but they do it asynchronously to minimize the dependency between them.

If you want more information, check out Confluent Developer where you will find courses to help you build event-driven microservices in a variety of languages.

If you aren't already on Confluent Developer, head there now using the link in the video description below.

And pop a comment below letting us know what else you'd like to talk about.

Don't forget to like, share, and subscribe.

And, thanks for watching.