DEV Community

Antonis Panos
Antonis Panos

Posted on

Exploring a new communication pattern for micro-frontends

I’ve been thinking about how, in the backend, we often use different types of communication based on our needs. For instance:

  • Events: Notify other parts of the system about changes without expecting a response.
  • Message queues: Decouple services and ensure reliable message delivery.
  • REST APIs: Direct, stateless communication using standard HTTP requests.

Each method serves distinct purposes, highlighting the need for diverse communication strategies depending on the context.

On the frontend, we usually rely on pub-sub, events, global state, and initializing state via root-level props. But sometimes, we need to perform a blocking request where one micro-frontend waits for a response from another.

I’ve added a new package on NPM that does just that. Trust me, this is not a promotion; I mostly want to hear your thoughts on this. It’s called Waiter, and you can see how it’s used here.

Use case examples

  • Tech-stack transitioning and centralized state management: This is a real case I encountered. We were slowly migrating a complex view from Angular to React, with components being replaced incrementally. Once everything was extracted, we operated a normal React app, and the Angular parent could be discarded. During the transition, React components were wrapped in angular wrappers, acting as intermediates for state changes. This required framework-specific adjustments, and re-renders were a threat that needed to be tackled. While we found a solution, the development experience wasn’t great, and the flow wasn’t straight-forward. An interface with controllers, and blocking requests would have provided a much better experience on a page with a disconnected state where complex validations and aggregations take place.
  • Race conditions due to asset resolution and bootstrapping: A micro-front’s assets are loaded asynchronously, yet a CTA on the host app (which is rendered first) dispatches events that must be handled by the micro-front, which might not be rendered yet. This results in a bad UX since nothing happens, and it’s hard to implement a mechanism that makes each app aware of the other. With this approach, we now have the ability for retries and even health checks to tackle such situations.

Why not use APIs or Events?
While APIs are great for server-to-client communication, they are not an option for inter-microfrontend interactions involving only local state which isn’t yet reflected on the backend. Similarly, events can notify other parts of the application about changes, but they don’t handle response data well or manage direct dependencies.

Framework agnostic
A significant advantage of this new approach is its framework agnosticity. Whether you’re working with React, Vue, Angular, or even vanilla JS, you can implement this pattern.

Beyond Pub/Sub and Event Listeners
This method reduces the reliance on Pub/Sub systems or event listeners that typically store incoming messages in the receiver’s state, leading to bloated and hard-to-manage state logic. It can even support complex orchestration patterns like sagas, where a series of user actions are handled in a transaction-like manner.

What it isn’t

  • It’s not a replacement for pub-sub or events. When no acknowledgment or response is required, there is simply no need for this.
  • It’s not a replacement for API calls. It should fetch only local state.

Links

I look forward to your thoughts!

Cheers!

GitHub logo antonisPanos / waiter-js

A simple module that enables you to perform and handle requests among your apps on the browser

 __     __     ______     __     ______   ______     ______
/\ \  _ \ \   /\  __ \   /\ \   /\__  _\ /\  ___\   /\  == \
\ \ \/ ".\ \  \ \  __ \  \ \ \  \/_/\ \/ \ \  __\   \ \  __&lt
 \ \__/".~\_\  \ \_\ \_\  \ \_\    \ \_\  \ \_____\  \ \_\ \_\ 
  \/_/   \/_/   \/_/\/_/   \/_/     \/_/   \/_____/   \/_/ /_/ 
                                                               

A simple module that enables you to perform and handle
requests among your apps on the browser.

🧭 Overview

It allows micro-frontends to handle requests and responses, simplifying application flow and data exchange between components.

🚀 Features

  • Dead simple: Handles requests and responses with a simple interface, simplifying application flow and data exchange between components.
  • Controller-based: Organizes requests using controllers for better manageability.
  • Promise-based API: Facilitates async programming with promises.
  • Lightweight: Is a lightweight library with no dependencies, making it…

Top comments (0)