Skip to content

feat: enforce global singleton for all exposed modules via module federation #308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

aachik-trimoz
Copy link

Purpose

This PR introduces a robust singleton enforcement mechanism for all modules exposed via module federation in this package. The goal is to guarantee that, regardless of how or where an exposed module is imported (host, remote, or multiple remotes), only a single instance of that module is ever created and used within the same global context (e.g., browser window).

Background

Previously, the plugin relied solely on the ES module system and browser module cache to ensure singleton behavior for exposed modules. However, in complex scenarios such as hot module replacement, dynamic imports, or certain micro-frontend setups, there was a risk that a module could be re-executed, resulting in multiple instances of what should be a singleton.

Why This Matters

Prevents duplicate singletons: Ensures that only one instance of critical modules (like HTTP clients, stores, or other stateful objects) exists per global context.
Improves reliability by reducing subtle bugs and inconsistencies that can arise from multiple instances, especially in large or dynamic micro-frontend architectures.
Transparent to consumers: No changes are required in consumer code; the singleton logic is handled automatically by the federation plugin.

This PR introduces a robust singleton enforcement mechanism for all modules exposed via module federation in this package. The goal is to guarantee that, regardless of how or where an exposed module is imported (host, remote, or multiple remotes), only a single instance of that module is ever created and used within the same global context (e.g., browser window).
@aachik-trimoz aachik-trimoz changed the title Update virtualExposes.ts feat: enforce global singleton for all exposed modules via module federation Jun 19, 2025
@gioboa
Copy link
Collaborator

gioboa commented Jun 19, 2025

Thanks for your proposal @aachik-trimoz
I don't have a strong opinion on this but I am wondering how it's managed by webpack or Rspack this kind of scenario cc @ScriptedAlchemy

@@ -4,23 +4,31 @@ export const VIRTUAL_EXPOSES = 'virtual:mf-exposes';
export function generateExposes() {
const options = getNormalizeModuleFederationOptions();
return `
const GLOBAL_KEY = '__MF_SINGLETONS__';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not add more globals, this entire thing already exists in FEDERATION
should be using loadShare('lib', {from: 'name of current app'}); or at least from share_scope_map off federation

This will also not work with module layers which is being introduced, where you can have concurrent parallel share scopes who still respect singletons, but within the scope - so in cases like next, i have 13 singletons for react.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the tests I have made, the instance is not unique, so the instance may be recreated. I'd like to know what your approach is to avoid the problem of multiple instances

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be 1 instance per app. Recreation of instances shouldn't happen.
If thats is happening theres a bigger problem

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance is stored in memory / state and registered in the federation global, we search for the instance by name.

These arch specs may be useful. https://github.com/module-federation/core/pull/3955/files#diff-5596198c9d0ae99e768d378410829b221f2ac61b098aa86e5c55a994d6efb964

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be the case for Webpack, but not for Vite. Based on my tests, I created an Axios instance with Keycloak for the token part and shared this instance with the other remotes. However, doing so resulted in the instances being created twice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah then its a bug in vite implementation. all webpack does is implement the runtime package.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gioboa what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can totally trust @ScriptedAlchemy

Copy link
Author

@aachik-trimoz aachik-trimoz Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gioboa do you think you can do an investigation to know why we don't have this in the Vite version

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Id check why instances are recreated. In webpack. We do have 1 aspect where we assign the instance associated with that runtime to webpack_require. So we can access the instance throughout the application.
However I can also call init() multiple times over and it doesn't recreate the instance because we store it in a scoped variable - but thats what /runtime init does.
We did introduce createInstance now as well to runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants