Skip to content

Microservices

FullstackCodingGuy edited this page Feb 18, 2025 · 32 revisions

Understanding

  • Microservices offer a streamlined approach to software development that accelerates deployment, encourages innovation, enhances maintainability, and boosts scalability. This method relies on small, loosely coupled services that communicate through well-defined APIs, which are managed by autonomous teams. Adopting microservices offers benefits, such as improved scalability, resilience, flexibility, and faster development cycles

Popular microservices patterns

Event Sourcing Pattern

Traditional systems only store the current state of data (e.g., a user’s account balance). But what if you need to know how that state changed over time? Event sourcing records every change as an event, giving you a full history.

It is like keeping the journal

Instead of overwriting data, every change is saved as an event in an event store. You can replay these events to see how the data evolved.

Best Practices:

  • Use tools like Apache Kafka for storing events.
  • Take periodic snapshots to speed up rebuilding the current state.
  • Ensure events are immutable and well-documented.

Strangular Pattern

Developers mostly use the strangler design pattern to incrementally transform a monolith application to microservices. This is accomplished by replacing old functionality with a new service — and, consequently, this is how the pattern receives its name. Once the new service is ready to be executed, the old service is “strangled” so the new one can take over.

To accomplish this successful transfer from monolith to microservices, a facade interface is used by developers that allows them to expose individual services and functions. The targeted functions are broken free from the monolith so they can be “strangled” and replaced.

To fully understand this specific pattern, it’s helpful to understand how monolith applications differ from microservices.

𝐀𝐏𝐈 𝐆𝐚𝐭𝐞𝐰𝐚𝐲 𝐏𝐚𝐭𝐭𝐞𝐫𝐧

Centralizes external access to your microservices, simplifying communication and providing a single entry point for client requests.

The API Gateway acts as a single entry point. Instead of connecting to multiple services, the client sends one request to the gateway, and it routes the request to the right services.

Aggregator Pattern

An aggregator design pattern is used to collect pieces of data from various microservices and returns an aggregate for processing. Although similar to the backend-for-frontend (BFF) design pattern, an aggregator is more generic and not explicitly used for UI.

To complete tasks, the aggregator pattern receives a request and sends out requests to multiple services, based on the tasks it was assigned. Once every service has answered the requests, this design pattern combines the results and initiates a response to the original request.

𝐁𝐚𝐜𝐤𝐞𝐧𝐝𝐬 𝐟𝐨𝐫 𝐅𝐫𝐨𝐧𝐭𝐞𝐧𝐝𝐬 𝐏𝐚𝐭𝐭𝐞𝐫𝐧 (𝐁𝐅𝐅)

Creates dedicated backend services for each frontend, optimizing performance and user experience tailored to each platform.

read more

BFF Provides Exclusivity - in providing features based on the client type (web/mobile/partner integration)

Think of the user-facing application as being two components — a client-side application living outside your perimeter and a server-side component (BFF) inside your perimeter. BFF is a variant of the API Gateway pattern, but it also provides an additional layer between microservices and each client type separately. Instead of a single point of entry, it introduces multiple gateways. Because of that, you can have a tailored API that targets the needs of each client (mobile, web, desktop, voice assistant, etc.), and remove a lot of the bloat caused by keeping it all in one place. The below image describes how it works.

image

Why BFF?

Decoupling of Backend and Frontend​ for sure gives us faster time to market as frontend teams can have dedicated backend teams serving their unique needs. The release of new features of one frontend does not affect the other.

We can much easier maintain and modify APIs​ and even provide API versioning dedicated for specific frontend, which is a big plus from a mobile app perspective as many users do not update the app immediately.

Simplify the system from Frontend and Backend perspectives​ as we don’t need any compromise.

The BFF can benefit from hiding unnecessary or sensitive data before transferring it to the frontend application interface, so keys and tokens for 3rd party services can be stored and used from BFF.

Allows to send formatted data to frontend and because of that can minimalize logic on it.

Additionally, give us possibilities for performance improvements and good optimization for mobile.

Fault tolerance in BFF

Fault tolerance is the most significant power of BFF allows us to handle most of the problems on the BFF layer instead of the client (frontend). In case of any microservice change, it can be controlled by all BFFs without emergency deployment of frontend clients. We all know that update of the mobile application on both stores is not an easy operation. It requires some additional time like review, which is not easy to estimate, and sometimes even its output can be a surprise — rejection. With BFF solution, we can cover versioning and backward compatibility separately per each client (frontend). Whole fault tolerance and strategies for that can be covered and managed in the BFF layer. For example, in our system, we can introduce separate BFF for each mobile client and avoid problems when one of them has some issues that affect the whole system, such as doing some self DDoS. In that case, we can disconnect this BFF from the system and investigate the problem inside of it without affecting the rest of the system. This is for sure a good strategy for BFFs dedicated for 3rd party services.

image

When to use BFF?

  • Consider BFF when you plan to extend the number of frontend types, it will make sense if and when you a significant amount of aggregation required on the server side.

  • If your application needs to develop an optimized backend for a specific frontend interface or your clients need to consume data that require aggregation on the backend, BFF is for sure a suitable option. Of course, you might reconsider if the cost of deploying additional BFFs’ services is high, but even then the separation of concerns that a BFF can bring make it a fairly compelling proposition in most cases.

  • https://medium.com/mobilepeople/backend-for-frontend-pattern-why-you-need-to-know-it-46f94ce420b0


𝐒𝐞𝐫𝐯𝐢𝐜𝐞 𝐃𝐢𝐬𝐜𝐨𝐯𝐞𝐫𝐲 𝐏𝐚𝐭𝐭𝐞𝐫𝐧

Enables microservices to dynamically discover and communicate with each other, simplifying service orchestration and enhancing system scalability.

𝐂𝐢𝐫𝐜𝐮𝐢𝐭 𝐁𝐫𝐞𝐚𝐤𝐞𝐫 𝐏𝐚𝐭𝐭𝐞𝐫𝐧

Implements a fault-tolerant mechanism for microservices, preventing cascading failures by automatically detecting and isolating faulty services.

When a service fails or becomes slow, it can cause the entire system to break down. For example, if a payment service is down, all orders might fail. The Circuit Breaker Pattern helps by stopping calls to a failing service until it recovers.

A circuit breaker acts like a switch:

  • Closed State: Requests go through as usual.
  • Open State: Requests are blocked to protect the system.
  • Half-Open State: Limited requests are sent to check if the service is working again.

Best Practices:

  • Combine with retries for temporary failures.
  • Monitor metrics to tune thresholds.
  • Provide meaningful fallbacks so users aren’t left hanging.

𝐑𝐞𝐭𝐫𝐲 𝐏𝐚𝐭𝐭𝐞𝐫𝐧

Enhances microservices' resilience by automatically retrying failed operations, increasing the chances of successful execution and minimizing transient issues.

read
  • The retry with backoff pattern improves application stability by transparently retrying operations that fail due to transient errors.

  • In distributed architectures, transient errors might be caused by service throttling, temporary loss of network connectivity, or temporary service unavailability. Automatically retrying operations that fail because of these transient errors improves the user experience and application resilience. However, frequent retries can overload network bandwidth and cause contention. Exponential backoff is a technique where operations are retried by increasing wait times for a specified number of retry attempts.

Applicability

Use the retry with backoff pattern when:

  • Your services frequently throttle the request to prevent overload, resulting in a 429 Too many requests exception to the calling process.

  • The network is an unseen participant in distributed architectures, and temporary network issues result in failures.

  • The service being called is temporarily unavailable, causing failures. Frequent retries might cause service degradation unless you introduce a backoff timeout by using this pattern.

Issues and considerations

  • Idempotency: If multiple calls to the method have the same effect as a single call on the system state, the operation is considered idempotent. Operations should be idempotent when you use the retry with backoff pattern. Otherwise, partial updates might corrupt the system state.

  • Network bandwidth: Service degradation can occur if too many retries occupy network bandwidth, leading to slow response times.

  • Fail fast scenarios: For non-transient errors, if you can determine the cause of the failure, it is more efficient to fail fast by using the circuit breaker pattern.

  • Backoff rate: Introducing exponential backoff can have an impact on the service timeout, resulting in longer wait times for the end user.

Example

image

𝐒𝐢𝐝𝐞𝐜𝐚𝐫 𝐏𝐚𝐭𝐭𝐞𝐫𝐧

Attaches additional components to your microservices, providing modular functionality without altering the core service itself.

𝐒𝐚𝐠𝐚 𝐏𝐚𝐭𝐭𝐞𝐫𝐧

Manages distributed transactions across multiple microservices, ensuring data consistency while maintaining the autonomy of your services.

A saga is a series of local transactions. In microservices applications, a saga patterncan help maintain data consistency during distributed transactions.

The saga pattern is an alternative solution to other design patterns that allows for multiple transactions by giving rollback opportunities.

When multiple services are involved in a transaction, things get tricky. Imagine placing an order — one service deducts money, another updates inventory, and another creates the order. What if one step fails? The Saga Pattern ensures that all steps work together or everything rolls back cleanly.

The Saga Pattern breaks a transaction into smaller steps. Each service handles its step and then triggers the next one. If something goes wrong, it can undo its step.

Types of Sagas:

  • ** Choreography**: Each service knows what to do next.
  • Orchestration: A central controller decides what happens next.

Best Practices:

  • Ensure each step can handle retries without errors.
  • Use logs to track what happened in case of issues.
  • Test your sagas thoroughly to avoid data inconsistencies.

𝐂𝐐𝐑𝐒 (𝐂𝐨𝐦𝐦𝐚𝐧𝐝 𝐐𝐮𝐞𝐫𝐲 𝐑𝐞𝐬𝐩𝐨𝐧𝐬𝐢𝐛𝐢𝐥𝐢𝐭𝐲 𝐒𝐞𝐠𝐫𝐞𝐠𝐚𝐭𝐢𝐨𝐧) 𝐏𝐚𝐭𝐭𝐞𝐫𝐧

Separates the read and write operations in a microservice, improving performance, scalability, and maintainability.

image

Best Practices

  • Focus on UI/UX and data needed by them.
  • Don’t try to make everything generic from the beginning; this may cause that component to be used across organizations and many people will want to contribute.
  • Use particular feature first over generic usage strategy. This is the best approach to keep clean API dedicated to one client.
  • Use the gold rule of three - Refer here for definition.

References

Clone this wiki locally