AI 资讯
The browser only talks to one server — composing Marko, React, and Riot into one hotel page
You open a hotel page. It looks like one product: a search grid, a featured stay, local highlights, reviews, a sticky trip summary. Under the hood it is eight HTTP servers and three UI runtimes . That is the experiment behind HarborStay , a demo booking app I built to answer a stubborn question: Can independent teams ship independent UI, in independent frameworks, and still give the browser a single, paint-ready HTML page? The punchline: yes — if the shell never imports a component. It only fetches HTML. The one rule The browser never talks to a fragment. It talks to the composer on port 3100 . The composer owns routes, layout, and the booking flow. Everything else is a fragment server that returns a chunk of HTML. flowchart LR Browser["Browser"] --> Composer["Composer :3100"] subgraph fragments["Fragment servers"] Nav["Navigation Marko :3101"] Search["Hotel search Marko :3102"] Details["Hotel details Marko :3103"] Reviews["Reviews Marko :3104"] Recs["Recommendations Marko :3105"] Highlights["Local highlights React :3106"] Disco["Experiences discovery Riot :3107"] Itin["Experiences itinerary Riot :3108"] end Composer --> Nav Composer --> Search Composer --> Details Composer --> Reviews Composer --> Recs Composer --> Highlights Composer --> Disco Composer --> Itin Composer --> CDN["CDN :3200"] This is the opposite of the usual microfrontend story (Module Federation, shared React, a host that import() s widgets). HarborStay is HTML composition . The shell does not know whether a fragment was rendered by Marko, React, or a hand-rolled Riot string. It only knows a URL. That one constraint buys a lot: Fragment teams can pick a runtime without asking the shell. A fragment outage becomes a fallback box, not a blank page. You can deploy search without redeploying reviews. It also forces honesty. If two fragments need to share a Redux store, the architecture is already leaking. What the user actually sees HarborStay models a small premium catalog: Harbor View Lodge in Lisbon
AI 资讯
Ownership, and Making This Template Your Own (Part 5)
Part 4 covered how this platform actually ships — scaffolding, CI/CD, and the two deployment shapes. This closing part is the two things every one of the last four parts has assumed: who actually owns each piece of this, and what it takes to make this whole template yours. Who owns what Every piece of this platform belongs to exactly one team, and that split is what makes independent deploys survive contact with a real organization, not just a single-team demo: Piece Owned by Depends on Host / Shell Platform team Store, Components, the manifest, the identity provider Components MFE Platform / design-systems team Nothing (a leaf) Store MFE Platform team The identity provider Utilities MFE Platform team Nothing (a leaf) Domain MFE (×N) Domain team Components, Store, Utilities only Manifest Registry Platform team Nothing Identity provider(s) Outside the platform — whichever the deployment configures — Backend / BFF Domain team, or a shared gateway (Part 2) Each team's own data The rule underneath the table: domain teams never import from each other, only from the shared platform layer. That keeps the dependency graph a strict two-level tree — Host → platform layer → domain leaves — instead of a mesh, which is what keeps independent deployability tractable once there's more than a handful of domain teams. It's the same rule that made every part of this series possible to write in isolation: Part 3's auth flow doesn't need to know Part 4's deploy pipeline exists, and neither needs to know how many domain teams there eventually are. Making this template your own Everything organization-specific in this platform — branding, which identity provider(s) it trusts, where the manifest lives — has lived in one file across this entire series, on purpose: // platform.config.json { "orgName" : "acme-corp" , "branding" : { "primaryColor" : "#0B5FFF" , "logoUrl" : "..." }, "idp" : { "issuers" : [ { "id" : "primary" , "issuer" : "https://issuer.example.com" , "clientId" : "..." , "def
AI 资讯
Why Module Federation — Building an Enterprise MFE Platform (Part 1)
This series walks through an actual enterprise microfrontend platform, end to end: one Host shell, three shared platform microfrontends, a manifest-driven mechanism for mounting any number of independently-owned domain microfrontends, a full OIDC auth flow, and a CI/CD pipeline. Every code snippet in this series is real and traceable to the actual boilerplate it's built from, on GitHub . Part 1 is the decision everything else in this series depends on: why Module Federation , and not one of the two other credible options. The one requirement that rules everything else out Strip away the buzzwords, and an "enterprise microfrontend platform" only has to guarantee one thing: a team ships a change to their part of the app without anyone else redeploying anything. Not "in theory, with enough coordination" — actually, mechanically, true. If shipping one team's bug fix requires a platform team to cut a release, this isn't microfrontends — it's a monolith with extra steps. That single requirement rules out more than it looks like it should. It rules out compiling every team's code into one shared build (that's just a single-page app with more steps). And it rules out anything where the Host app needs to know, at its own build time, which teams' pages exist and which version of each — because "known when the Host was built" and "deployed independently of the Host" are opposites. The decision Use Webpack 5 Module Federation , in runtime-composition mode, as the platform's way of putting every team's page together into one app: The Host ships with an empty list of remote apps built in. Instead, it looks up every team's page from a small list — a manifest — that it fetches fresh every time the app loads. React, the shared state layer, and the shared design system are all declared as singletons : every team's page gets the exact same running instance of each, not its own separate copy. "Deploying" a team's page means adding or updating one entry in that manifest. The Host itself
AI 资讯
Micro-Frontends, One Year On: The Workarounds That Made Single-SPA Reliable for Us
A year ago, I wrote about our first year with micro-frontends . It was a frank retrospective: the win was framework autonomy and independent deploys, the cost was 20 GB of RAM during local dev, 15–20 minute deploy cycles, and a monorepo that became its own coordination tax. The closing line was the honest one — if I could choose again, I would think carefully about using Microfrontend or not. This article is the follow-up the previous one didn't write: what we actually built around Single-SPA to make it work. Not the architecture diagram. The workarounds. The dead code that's not really dead. The regexes we wish we didn't need. The .NET app no Single-SPA tutorial mentions. The browser-side singletons we use as message buses. The dev experience we documented and the parts we didn't. If you're evaluating Single-SPA in 2026, this is the article I wish I'd had a year ago. Where we left off The platform is a Single-SPA monorepo with seven in-repo apps and five shared packages. On top of that sit roughly ten standalone repositories for newer features, loaded at runtime via SystemJS import maps. Shared dependencies — React 18.2, ReactDOM, axios, react-bootstrap, single-spa 5.9.5 — are served from CDN and externalized in every webpack config. The architecture is textbook. The reality, a year in, is that the textbook stops where the workarounds start. The shell isn't where you think it is If you read our frontend repo you'd conclude the shell is whatever directory has single-spa in its package.json . That's where Single-SPA gets configured. That's where start() is called. There's even a webpack entry that, until recently, was producing an index.ejs HTML template. That HTML template is dead code. The real shell — the HTML the browser actually receives, in every environment — is rendered by an ASP.NET MVC application, in a Razor view. The frontend repo ships JavaScript modules. The .NET host ships the page that loads them. That's the unlock. Once you see this, every other work