Skip to main content

Microfrontends

Context

Microservices are being adopted. Each microservice potentially delivers its own user interface. The existance of multiple user interfaces should be hidden from the user.

Problem

  • The user interfaces are not integrated to provide the user a cohesive product.

Solution

Integrate the user interfaces by pulling them together in a shell user interface. Each microservice serves UI components - the microfrontends - that are loaded in the shell user interface at runtime. The separate user interfaces will be perceived as one cohesive product.

There are different technical solutions to implement this technique. One aspect of consideration when choosing a technology is the degree of isolation each microfrontend should have. The more isolated, the potentially bigger will be the bundle size as the isolation might allow for different user interface frameworks and versions. The technical solutions range from simple AJAX-based loading of components and sharing global framework objects to using web components. One of our interviewees pointed us to Webpack's Module Fedaration feature as a gamechanging technology.

This approach works well for applications where the existance of multiple user interfaces has to be hidden from the user. Otherwise, consider using UI suites.

Maturity

Proposed, to be evaluated.

Sources of Evidence

L14:

  • Verticals at otto.de
  • all pages of shop contain fragments from different verticals
    • principle of "page assembly proxy" used to integrate them
      • fragments not part of primary content
      • initially invisible and preferably integrated at client side using AJAX
      • e.g. shopping cart preview fragment included in almost every page
      • primary content integrated at server side using Edge Side Includes => resolved by Varnish Reverse Proxy
    • (+) user experience: consistent entity despite backend decomposition

L16:

  • independent services might provide their UI fragments, e.g. HTML5 fragments
    • can be put together into one dynamic panel
    • => no need for centralized for front-end

L34:

  • data format changes => need to be reflected on UI
    • difficult since components maintained by distinct teams
    • UI only offers limited type safety
  • Cites [22] => meta-programming and aspect-oriented programming
    • let meta-information stream from source service to integration component
    • JS lib weaves provided information and templates together at runtime to assemble UI
    • (+) minimize maintenance efforts since change immediately reflected in UI fragment
    • (+) goes well with framework as Angular or React
    • meta-information allows rendering UI fragments on various platforms using native components

LM45:

  • Context: interviews and insights from multiple cases on technologies and sw quality in MSA
  • 9 apps using SPAs
  • some saw SPAs be problematic with microservices
    • multiple requests per page to multiple services => large amount of business logic for data integration on client-side and low performance
    • favored dynamic server-side rendering
      • Server Side Includes (SSI)
      • Edge Side Includes (ESI)
      • others employ the backend for frontends pattern

Interview B:

  • Micro-frontends allow to deliver service-specific UIs

Interview E:

  • Microfrontend = continuation microservice idea to the frontends
    • easier in backend because bundle size not an issue
    • deliberately use framework and framework versions + isolation over process boundaries
    • frontend: only one process
    • frontend: bundle size plays a role
  • Shell approach: load many small frontends into a larger solution at runtime
    • no compile time dependencies but only runtime dependencies
    • different approaches
      • Index HTML and load different SPAs + some meta logic on when to show and hide them
        • "meta routing": another router over the other routers responsible for showing/hiding the SPAs
      • Web components
        • can be loaded via lazy loading
        • maybe overrated; doesn't matter if loading SPA directly or web component that wraps ab SPA
        • shadow DOM very useful for isolation
    • trade-off between isolation and budle size
      • potentially multiple frameworks and/or their versions
      • goal: small budnle size => potentially share framework
        • load Angular once into global namespace and then let every application use it => breaking the isolation, dependent on what shell offers
        • same tradeoff when sharing UI widgets: code redundancy vs. isolation
        • same tradeoff when communicating: encapsulation vs. redundant user interactions
  • DDD comes before the decision to do micro-frontends
    • each microfrontend responsible for a subdomain
  • Communication between micro-frontends
    • if shell solution: easy bus system
      • shared object with API to send and subscribe to messages
      • additional logic to support lazy loading: caching so that lazy loaded modules can get the whole complex
  • Comparison to UI suites
    • use shell solution if hiding from user that multiple applications
  • Context: user auth
    • get security token in shell, then share with message bus, session or local storage
  • Context: technology
    • webpack 5 module federation game changer for micro frontends since native support for it
    • smart concepts: allows configuring how to resolve different versions of a framework
      • reuse existing version, error, use own version
      • allows finding intermediate solutions between version upgrades quite easily
    • still: versioning is a problem since it increases the bundle size
      • sometimes hard to have dependencies of different versions
    • pointer to project internal standardization on a framework version;
      • technical solution not always the best way to solve such a problem