The microservice architecture pattern has revolutionized1 the way we build software today. But as we are seeing monolith after monolith being split into microservices in the backend, we are still shipping frontend code in large chunks; bundles, minifiers, domain shards, resource inlining is just a couple of techniques used to improve web performance.
Micro frontends describes an architectural pattern in which the frontend monolith is decomposed into smaller features fully owned by different cross-functional teams. This approach enables loose coupling and an autonomous workflow to ensure development, testing and deployment without the interference of external dependencies, enabling teams to individually scale delivery output.
When observing the backend transition towards microservices we’ve seen a lot of success stories, with increased innovation, resiliency, deliverability, and ability for easier scaling. But we’ve also seen an increased amount of complexity and latency. So in an area where speed is key, why would we want to add more complexity and latency to our frontends?
Although I’m heavily excited about the microarchitectures, a bit of pragmatism might be healthy; full microarchitecture has its trade-offs and is not a silver bullet, but I do think of a couple of reasons to consider decomposing parts of your frontend monolith.
Isolation and responsibility
A generic system typically contains a variety of moving parts, and it quickly becomes a complex task in identifying what and who is responsible for each thing. The microservice mindset is based on a particular solid principle: A service should be small, focused and doing one thing very well.
This is often a problem with font-end code as, with the DOM as runtime, all components within the interface has the ability to interact and affect each other, often unintended via side-effects.
One common way to decompose an application is to split the application into verticals, also know as self-contained systems. Each vertical is implemented by exactly one team and is responsible for a single domain with clear boundaries2. Hence, “the order vertical” has responsibility for the order process, “the search vertical” has responsibility for the search and so on.
While the most optimal way to describe boundaries of a service is debatable; it’s clear that this separation of concerns is necessary to obtain brevity. Not only does this introduce clarity in the aspect of responsibility, but also introduces a clear ownership of their respective domain.
Separation of concerns is necessary to obtain brevity.
I am a firm believer that a team should have the mandate to work autonomously and not enforced with strange methodologies, frameworks, and other limitations. That said, I also believe in collaboration and working towards a common goal.
By leveraging autonomy and loose coupling, the team can be truly technology agnostic, free to select whichever tooling, language, framework, or methodology that is suitable for their needs without imposing on other teams.
Decomposing for autonomy also introduces a clear ownership of each feature, which in turn enables additional autonomy regarding deployment, maintenance, monitoring, and deprecation, increasing the ability to continuously deliver functionality into production.
By decomposing the monolith into smaller pieces, the ability to add individual scaling and component specific cache policies also increase fundamentally. Imagine the ability to automatically allow offloading of backend services in the frontend on traffic peaks.
Although it probably won’t solve all performance issues, HTTP/2 is probably mandatory in order to succeed with micro frontends. By including performance optimizations, such as multiplexing, header decompression, binary format instead of text, and stream dependencies; the technology is essentially designed for better performance.
This means no more bundles and no need for domain sharding3 to serve clients for performance, but ultimately be more performant when sliced into and smaller files45.
Decomposing the frontend monolith can gain improvements in isolation, deliverability and performance. Although while it may add extra complexity into an application, it may also increase clarity.
By investing pragmatically in this technique I do believe there are additional benefits other than described in this post. And although if the monolith keeps on living, the principles circling microarchitecture could be of use in a non-micro architecture.
Gain more insights into micro frontends:
- Microservice Websites (The manifesto)
- code.talks 2017 - The Recipe For Scalable Frontends (Zalando)
- Microservice Websites by Gustaf Nilsson Kotte (@ Øredev)
- RSconf2018. Introduction to Micro Frontends
- Michael Geers - Micro Frontends: Break Up You Web App!
- microXchg 2016 - Stefan Tilkov: Wait, what!? Our microservices have actual human users?
- microXchg 2018 - Micro Frontends - breaking down the last monolith - Matthias Laug
- While Service Oriented Architecture (SOA) have been around for ages it’s not the same as microservice architecture (MSA). Microservices vs SOA – whats the difference ↩
- Scaling with Microservices and Vertical Decomposition ↩
- High Performance Browser Networking: HTTP/2, Domain sharding, Concatenation and Spriting ↩
- HTTP/2 Demo: Akamai http/2 demo ↩
- HTTP/2 Demo: Gophertiles ↩