-
Notifications
You must be signed in to change notification settings - Fork 67
Complete Plan Demo #268
Description
Product Requirements Document (PRD): ESMX Micro-Frontend Architecture
| Project Name | ESMX (EcmaScript Module Exchange) |
1. Executive Summary
ESMX is a next-generation Micro-Frontend (MFE) Architecture designed to leverage Native ESM (EcmaScript Modules) standards. Unlike Webpack Module Federation which relies on build-time orchestration, ESMX utilizes the browser's native module system and Node.js file system linking to create a seamless, zero-bundler development experience.
Key Value Proposition:
- Zero-Bundler Dev: No Webpack/Vite config hell for federation.
- Isomorphic SSR: True Server-Side Rendering across micro-app boundaries.
- Singleton Dependencies: Guaranteed sharing of React/Vue instances via "Providers" (
ssr-npm-*). - Universal Routing: A single router state driving multiple framework apps.
2. Architecture Overview
2.1. The "Hub & Spoke" Model
The architecture consists of three distinct entity types:
- Hub (Host): The main shell application. It handles global layout, navigation, and authentication.
- Remotes (Micro-Apps): Independent applications (Vue 2, Vue 3, React) containing business logic.
- Providers (
ssr-npm-*): Special infrastructure packages that "provide" shared dependencies to the Hub and Remotes.
2.2. Visual Architecture
graph TD
User["Browser / Client"] --> Hub["Hub (Host)"]
subgraph providers ["Shared Providers (ssr-npm-*)"]
ReactProvider["ssr-npm-react"]
Vue2Provider["ssr-npm-vue2"]
Vue3Provider["ssr-npm-vue3"]
RouterCore["@esmx/router"]
end
subgraph remotes ["Micro-Apps (Remotes)"]
Vue2App["Vue 2 App"]
Vue3App["Vue 3 App"]
ReactApp["React App"]
end
Hub --> ReactProvider
Hub --> Vue2Provider
Hub --> Vue3Provider
Hub --> RouterCore
ReactApp --> ReactProvider
ReactApp --> RouterCore
Vue2App --> Vue2Provider
Vue2App --> RouterCore
Vue3App --> Vue3Provider
Vue3App --> RouterCore
Hub -- "Dynamic Import" --> ReactApp
Hub -- "Dynamic Import" --> Vue2App
Hub -- "Dynamic Import" --> Vue3App
3. Core Mechanism: "The Bridge" (Module Linking)
ESMX uses a centralized configuration file in every package: entry.node.ts. This file acts as the Resolver Map.
How Import Redirection Works
When you write import React from "react" in a Remote App:
- Naive Expectation: Node/Browser looks in
node_modules/react. - ESMX Reality: The
entry.node.tsintercepts this import. - Redirection: It maps "react" to the Provider Path (e.g.,
../ssr-npm-react). - Result: All apps import React from the exact same file path on disk/network. This guarantees a Singleton Instance.
4. Universal Routing System
Standard react-router-dom or vue-router manage their own window.history state. If used independently in Micro-Frontends, they conflict (one router undoes the other's changes).
ESMX Solution: A Universal Router (@esmx/router) that sits above the frameworks.
4.1. Core Router (@esmx/router)
- Role: Singleton State Manager.
- Location:
packages/router - Key Class:
Router. - Responsibilities:
- Listen to
popstate. - Manage Route Transition Lifecycle (
beforeEach,afterEach). - Orchestrate "Layers" (Modals/Overlays managed by URL).
- Listen to
4.2. Framework Adapters
Apps do not interact with the Core Router directly for rendering. They use Adapters that feel native to their framework.
A. React Adapter (@esmx/router-react)
RouterView: Renders the component associated with the current route.- Features: Nested routes support, depth tracking context.
RouterLink: A replacement for<a>tags.- Props:
to(string/object),replace(bool),activeClass(string). - Behavior: Prevents full page reload; calls
router.push().
- Props:
B. Vue Adapter (@esmx/router-vue)
- Global Components: Registers
<router-view>and<router-link>. - Injection: Provides
$routerand$routeto all components.
5. The "Universal" Architecture Specification
This section describes the Full Capabilities of the ESMX framework. While the current implementation in examples/router-demo is a partial reference, the core packages support building a complete Universal Super App.
5.1. Supported Frameworks
Using the adapters in packages/, you can mix and match essentially any framework. The following are supported out-of-the-box:
- Vue 2 & 3: Fully supported via
@esmx/router-vue. - React 18+: Fully supported via
@esmx/router-react. - Others: Extensible architecture allows adding Preact/Solid logic easily.
5.2. Detailed Provider Specifications
Providers are the glue of the system. You can create as many as needed.
A. React Provider (ssr-npm-react)
Status: Architecture Ready (Needs to be created by User)
To enable React support in the Hub, you would create this provider.
ssr-npm-react/src/entry.node.ts
import type { EsmxOptions } from '@esmx/core';
export default {
modules: {
links: { 'ssr-npm-base': './node_modules/ssr-npm-base/dist' },
imports: {
'@esmx/router': 'ssr-npm-base/@esmx/router' // Inherit Router
},
exports: [
"pkg:react",
"pkg:react-dom",
"pkg:@esmx/router-react" // The crucial adapter
]
}
} satisfies EsmxOptions;B. Vue Providers (ssr-npm-vue2 / ssr-npm-vue3)
Status: Available in Reference Implementation
These export vue and @esmx/router-vue specific to their versions.
6. Directory Structure: The "Super App" Blueprint
This is the recommended structure to build a fully featured Application leveraging ALL capabilities (Vue + React + Shared State). This extends beyond the basic router-demo.
my-super-app/
├── ssr-hub/ # [HOST] Main Shell (Layout, Nav, Auth)
│ ├── src/entry.node.ts # LINKS: ssr-vue3, ssr-react, etc.
│ └── ...
│
├── ssr-react/ # [REMOTE] New React Micro-App
│ ├── src/entry.node.ts # IMPORTS: react from ssr-npm-react
│ └── ...
│
├── ssr-vue2/ # [REMOTE] Vue 2 Micro-App
│ └── ...
│
├── ssr-vue3/ # [REMOTE] Vue 3 Micro-App
│ └── ...
│
├── ssr-npm-base/ # [PROVIDER] Infrastructure (Core, Router, State)
│ └── src/entry.node.ts # Exports: @esmx/core, @esmx/router, @esmx/class-state
│
├── ssr-npm-react/ # [PROVIDER] React Framework Definition
│ └── src/entry.node.ts # Exports: react, @esmx/router-react
│
├── ssr-npm-vue2/ # [PROVIDER] Vue 2 Framework Definition
│ └── ...
│
├── ssr-npm-vue3/ # [PROVIDER] Vue 3 Framework Definition
│ └── ...
Note on Existing Examples
The folder examples/router-demo is a partial implementation showing only Vue 2 + Vue 3 integration. To achieve the full "Super App" described above, you simply need to duplicate the Vue patterns and apply them to React using the @esmx/router-react package that is already installed in the workspace.
6.1. Unified Routing Strategy (Crucial)
In this "Super App", you cannot use the standard vue-router or react-router-dom packages directly in your remotes, as they would conflict and fight for control of the browser URL.
Instead, the architecture enforces a Singleton Router:
- The Source of Truth: The
@esmx/routerinstance lives inssr-npm-base. It is the only thing allowed to touchwindow.history. - The Hub's Role: The
ssr-hubinitializes this router and defines the Master Route Map./vue-> Loadssr-vue3/react-> Loadssr-react
- The Adapters:
- React Remote: Uses
@esmx/router-react. It provides<RouterLink>&<RouterView>that look like React Router but actually talk to the specific ESMX singleton. - Vue Remote: Uses
@esmx/router-vue. It provides<router-link>&<router-view>compatible with Vue ecosystem.
- React Remote: Uses
Flow of a Navigation Event:
- User clicks a link in the React Sidebar.
@esmx/router-reactintercepts the click.- It calls
router.push('/vue-page')on the Shared Router. - The Shared Router updates the URL.
- The Shared Router notifies the Vue Adapter.
- The Vue Main Area updates to show the new page.
7. Development Guidelines
7.1. Adding a Shared Library (e.g., lodash)
Problem: If vue2-app imports lodash and react-app imports lodash, the browser downloads it twice.
Solution: Create a Provider.
- Create Folder:
examples/micro-frontend-demo/ssr-npm-lodash - Install:
npm install lodashinside that folder. - Configure:
src/entry.node.ts: Export"pkg:lodash".
- Link: Update
hub/src/entry.node.tsto linkssr-npm-lodash. - Use: In any remote app,
import _ from 'lodash'. ESMX now routes this import to your shared folder.
7.2. SSR Data Fetching
ESMX treats SSR as a first-class citizen. Code runs identically on server and client.
- Server Lifecycle:
- Incoming Request ->
entry.node.ts(Hub). router.push(url).- Router matches
react-app. React.renderToString(<App />).
- Incoming Request ->
- Data Pre-fetching:
- We cannot use
useEffect(client-only). - Pattern: Use a static
asyncDataorloadermethod on your Route Component. - Note: The
@esmx/routersupports ametafield where you can attach data loaders.
- We cannot use
8. Migration Guides
8.1. React: react-router-dom to @esmx/router-react
| Feature | react-router-dom |
@esmx/router-react |
|---|---|---|
| Link | <Link to="/path"> |
<RouterLink to="/path"> |
| View | <Outlet /> / <Routes> |
<RouterView /> |
| Hook | useNavigate() |
const router = useRouter(); router.push() |
| Params | useParams() |
const route = useRoute(); route.params |
Code Example Refactor:
Before:
import { Link } from 'react-router-dom';
<Link to="/about" className="nav-link">About</Link>After:
import { RouterLink } from '@esmx/router-react';
<RouterLink to="/about" activeClass="active" className="nav-link">About</RouterLink>8.2. Vue: vue-router to @esmx/router-vue
The @esmx/router-vue package works for both Vue 2 and Vue 3, but the setup slightly differs.
| Feature | vue-router |
@esmx/router-vue |
|---|---|---|
| Link | <router-link to="/path"> |
<router-link to="/path"> (Same API) |
| View | <router-view> |
<router-view> (Same API) |
| Route Access | this.$route / useRoute() |
useRoute() (Vue 3) / matched.route (Vue 2) |
| Push | this.$router.push() |
useRouter().push() or global instance |
Vue 3 Setup Example:
import { createApp } from 'vue';
import { install } from '@esmx/router-vue';
import App from './App.vue';
export default (props) => {
const app = createApp(App);
app.use(install); // Installs $router, $route, and global components
return app;
};10. Additional Core Libraries
ESMX includes specialized packages to handle state, data fetching, and module resolution.
10.1. State Management: @esmx/class-state
A lightweight, class-based reactive state management solution.
- Key Features: Framework agnostic, TypeScript ready, built-in reactivity.
- Usage: Create classes that extend the state base and auto-update UI when properties change.
10.2. Data Fetching: @esmx/fetch
A standardized HTTP client wrapper around axios.
- Key Features:
- Built-in caching (
cachedir). - CLI progress bars (
cli-progress) for server-side tasks. - Standard interfaces for SSR data pre-fetching.
- Built-in caching (
10.3. Module Resolution: @esmx/import
The engine behind the "Bridge". It parses entry.node.ts and generates Import Maps to resolve pkg:* and root:* protocols, enabling the zero-config linking of Local modules.
11. Tooling Architecture
11.1. Build Engine: @esmx/rspack
While ESMX provides a "Zero-Config" experience, it uses Rspack under the hood for high-performance building and HMR (Hot Module Replacement).
- Role: Handles the transpilation of TS/TSX, CSS/Less processing, and standardizing assets.
- Integration: providing
createRspackReactAppandcreateRspackVue2Apphelpers used inentry.node.ts.
11.2. The Core: @esmx/core
The brain of the framework.
- CLI: Provides the
esmxcommand (esmx dev,esmx build). - Middleware: Intercepts requests to serve SSR'd HTML or raw modules.
- Render Loop: Orchestrates the server-side rendering process, injecting state and styles.
12. Appendix: Type Definitions
12.1. RouterLink Props (React)
interface RouterLinkProps {
to: string | { path: string, query?: Record<string, string> };
type?: 'push' | 'replace' | 'pushWindow';
exact?: 'exact' | 'include'; // specific matching logic
activeClass?: string; // class added when route matches
tag?: string; // default 'a'
}12.2. Wrapper Component (Vue 2/3)
For Vue, the structure is similar but uses slots.
<router-link to="/about" v-slot="{ href, navigate, isActive }">
<a :href="href" @click="navigate" :class="{ active: isActive }">About</a>
</router-link>