Skip to content

Complete Plan Demo #268

@gorillaworkout

Description

@gorillaworkout

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:

  1. Zero-Bundler Dev: No Webpack/Vite config hell for federation.
  2. Isomorphic SSR: True Server-Side Rendering across micro-app boundaries.
  3. Singleton Dependencies: Guaranteed sharing of React/Vue instances via "Providers" (ssr-npm-*).
  4. 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:

  1. Hub (Host): The main shell application. It handles global layout, navigation, and authentication.
  2. Remotes (Micro-Apps): Independent applications (Vue 2, Vue 3, React) containing business logic.
  3. 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
Loading

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:

  1. Naive Expectation: Node/Browser looks in node_modules/react.
  2. ESMX Reality: The entry.node.ts intercepts this import.
  3. Redirection: It maps "react" to the Provider Path (e.g., ../ssr-npm-react).
  4. 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).

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().

B. Vue Adapter (@esmx/router-vue)

  • Global Components: Registers <router-view> and <router-link>.
  • Injection: Provides $router and $route to 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:

  1. The Source of Truth: The @esmx/router instance lives in ssr-npm-base. It is the only thing allowed to touch window.history.
  2. The Hub's Role: The ssr-hub initializes this router and defines the Master Route Map.
    • /vue -> Load ssr-vue3
    • /react -> Load ssr-react
  3. 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.

Flow of a Navigation Event:

  1. User clicks a link in the React Sidebar.
  2. @esmx/router-react intercepts the click.
  3. It calls router.push('/vue-page') on the Shared Router.
  4. The Shared Router updates the URL.
  5. The Shared Router notifies the Vue Adapter.
  6. 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.

  1. Create Folder: examples/micro-frontend-demo/ssr-npm-lodash
  2. Install: npm install lodash inside that folder.
  3. Configure:
    • src/entry.node.ts: Export "pkg:lodash".
  4. Link: Update hub/src/entry.node.ts to link ssr-npm-lodash.
  5. 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:
    1. Incoming Request -> entry.node.ts (Hub).
    2. router.push(url).
    3. Router matches react-app.
    4. React.renderToString(<App />).
  • Data Pre-fetching:
    • We cannot use useEffect (client-only).
    • Pattern: Use a static asyncData or loader method on your Route Component.
    • Note: The @esmx/router supports a meta field where you can attach data loaders.

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.

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 createRspackReactApp and createRspackVue2App helpers used in entry.node.ts.

11.2. The Core: @esmx/core

The brain of the framework.

  • CLI: Provides the esmx command (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>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions