Micro frontend architecture using federated Remix apps #1989
Replies: 9 comments 22 replies
-
Hi @adrienbaron, thank you for your thoughtful and detailed comments here! Your explanation mostly makes sense to me, but I will defer to @jacob-ebey from our team who has worked extensively with module federation in webpack. He mentioned recently that he has some ideas on how we can address some of the same concerns that MF does but perhaps slightly differently. |
Beta Was this translation helpful? Give feedback.
-
Id suggest a slightly altered approach here, if its possible. Instead of a manifest.js -> id look at Jacobs work on ESbuild and module federation interop. If we are going to bring module federation to more frameworks and build chains, we should conform to a single API to allow for interoperability between toolchains. The get and init API from webpack has been pretty solid across vite and rollup already. {
get:(request)=>import(request),
init:(scope)=> {assignExternalDepsToInternalGraph}
} init is used to share dependencies between host and remote. Init runs once per remote and is the first thing called. After init. Any imports() are called via get() window.remote.get('./page').then(factory=>factory()).then((module)=>{ |
Beta Was this translation helpful? Give feedback.
-
adrienbaron Take a looke at https://github.com/namecheap/ilc |
Beta Was this translation helpful? Give feedback.
-
I'm quite new here, but I was wondering whether you solved for nested routing within your POC, or if your POC is purely focused on a route based MFE approach. I've yet to watch your recent talk @adrienbaron but I plan to, so you may have addressed it there. |
Beta Was this translation helpful? Give feedback.
-
I would love to see this become a first-party feature. I've recently learned a bit about module federation in angular (yep, we are stuck in angular at my job). and it basically sounded like iframes without the downsides of iframes. so this would be super cool to have in Remix! |
Beta Was this translation helpful? Give feedback.
-
@adrienbaron gave video talk on Microfrontends with Remix @ScriptedAlchemy (creator of module federation)- also gave a talk on the powers of module federation, which is tangentially related to microfrontend:
|
Beta Was this translation helpful? Give feedback.
-
Hi, I'm looking to write a web application which module federation would be a good fit for. Ideally I'd like to use Remix and was wondering if there had been any further progress or discussions on Remix supporting MF? This thread seemed like the best place to ask given all the relevant comments, but can shift this to somewhere more appropriate if needed! |
Beta Was this translation helpful? Give feedback.
-
I did some experimentation using @softarc/native-federation-core and the new vite compiler at jrestall/remix-federation. It successfully combines multiple independently deployed remix sites together at runtime by dynamically fetching remix's route manifest.json files as needed. It allows remix sites to expose react components and code for use in others at runtime. It could also be used to load any shared JavaScript code. The core mechanism is that when the user navigates to a splat route like There's a few caveats and areas for future investigation,
Seems to work pretty well otherwise. Feel free to check it out, critique the approach and/or suggest improvements. |
Beta Was this translation helpful? Give feedback.
-
If I have existing Module Federation(MF) build using webpack. And I want to re-use some of the MicroFrontend in my MF, is it possible to build a Remix and used existing MF build from webpack? |
Beta Was this translation helpful? Give feedback.
-
Hey there 👋!
I've been exploring how to get federated Remix apps working.
Edit:⚠️ Disclaimer: Seeing the term "federated", you might instantly think of Module Federation.
This is not what I'm advocating for here. Instead, I'm using federated to mean "Multiple Remix app deployed independently but acting as one", not taking into account whether it uses Module Federation under the hood.
My current POC doesn't use Module Federation at all.
I'm starting this discussion to try and get early feedback and input on changes that might be needed in Remix to get this to work.
What I think we want to achieve
From a high level perspective, I think we'd like to get one deployed Remix
host
serving multiple independently deployed Remixremotes
.Each
remote
would contribute someroutes
to the resulting Remix app. The resulting user experience should be indistinguishable from a single Remix app being deployed to a domain.This means:
Actions
,Loaders
andErrorBoundaries
behave as if all the route are from the same appIn the example below,
remote A
andremote B
are independently deployed from thehost
and contribute/checkout
and/account
respectively:Why do we want this?
For large organisation, there is a big benefit to have independent codebase.
For example at the company I work at (Cazoo), each team own a vertical piece of the domain (
Search
,Checkout
,Consumer Financing
...). Each team currently deploy their own independent Next.js app to a shared AWS API Gateway.✅ This is nice because team can work and deploy independently.
❌ However, this comes at a cost: each time a user cross a domain boundary, they are basically jumping to a complete different app.
This approach would allow companies like ours to keep teams independent, while having a single unified app at runtime 🎉!
General idea (How the POC works)
In my POC the way I went about is as follows:
host
is a deployed Remix app (using express), all server side code runs thereremotes
are bundled Remix apps, they do not need to have runtimesGetting SSR working
host
will load allremotes
build/index.js
dynamically.ServerBuild
export, and merge the routes with its ownServerBuild
.We now have a single
ServerBuild
and from Remix point of view there is no difference if it had been bundled together: Success 🎉!Getting client side working
For client side we need a few things:
manifest.js
file needs to be dynamic as it needs to contain the merged routes.@remix-run/react
to the remote client bundles.1.
manifest.js
I've simply added an endpoint to the host express app that returns the merged manifest from the
ServerBuild
.2. Client side bundles
I've made the express also serve the
public
folder from theremote
.3. Providing React version dynamically
That was the hardest bit (so far 😅).
Up to here, I didn't need to do a single change to Remix to get things working. But this required some change to the build system.
I have forked Remix and added this commit.
Let's go through what it does together:
isRemixMicroFrontend
option for the config that can behost
orclient
host
, we inject a shim in the client bundle that will expose the following dependencies onwindow.__remixDependencies
:react
react-dom
@remix-run/react
client
, we use a ESBuild plugin to consume the exposed dependencies fromwindow.__remixDependencies
.react
) by another one that export the same module exports, but coming from the object onwindow.__remixDependencies["react"]
...Anndd that's it 🎉.
Remote bundle now don't include those three dependencies anymore, and just use the ones exposed by the
host
.This means you know have full client side navigation, Remix hooks just work out of the box etc...
Where I want to go next
Finding the right place for fetching and merging
ServerBuild
My POC is well and good, but it requires a lot of faff outside of Remix to fetch and merge
ServerBuild
.This is a problem because even though the Express Remix adapter exposes this
require("./build")
call, that's not the case for all adapters.So I want to find the correct place to handle that logic.
Looking at Remix source, I can see that it's adapters usually work this way:
require("./build")
and some platform specific faff.createRequestHandler
to get a "pure" Remix request handler.I think it would probably be quite bad if every adapter needs to have some logic to handle this.
So here what I'm currently thinking:
What if
createRequestHandler
does the work?This would mean it would work automatically no matter the
adapter
, and for futureadapters
too 👌.So what would it entail?
It would fetch and merge
remotes
ServerBuild
First, we would add a new type of
ServerBuild
with exposes an array ofRemoteBuildFetcher
.Those
remoteBuildsFetchers
could be provided in a.server
file from thehost
. They abstract away the "how" we get thoseServerBuild
over the network.When
createRequestHandler
get this type of build, it calls each fetcher, merge theServerBuild
and use those to serve the app.To overcome the fact that
createRequestHandler
is notasync
, my thinking was that if the handler gets a request whileremote
ServerBuild
are still loading it would just stall it until everything is loaded.It would serve
/manifest.js
As the request handler is in control of the whole routing, it could serve the
/manifest.js
route dynamically based on the merged manifest it will have.🙇♂️ What do I need from you?
Feedback mostly!
🎉🎉 Thank you for reading me till the end! 🎉🎉
I know this is quite a massive explanation, but I feel like the actual changes needed in Remix are quite minimal for all the cool application it could unlock.
Side note: I'd like to thank everyone that has worked on writing this codebase. This was super easy to pickup and work on ❤️!
Beta Was this translation helpful? Give feedback.
All reactions