Achieving "Zero-Cliff" UI: How Framework M Solves Multi-Package Composition

Viewed 34

Hello Framework M Community! 🚀

We just finalized ADR-0010, and it addresses one of the biggest headaches in modern enterprise software: How do we support a distributed plugin architecture without forcing a massive "infrastructure tax" on developers?

In most frameworks, you have to choose early: do you want a simple Monorepo (easy DX, hard to scale) or a Micro-frontend (MFE) architecture (great scale, nightmare DX).

With Framework M, we’re introducing Zero-Cliff UI Composition.

What is "Zero-Cliff"?

It means your code doesn't care how it's deployed. Whether you are running a pnpm workspace on your laptop (Level 1) or serving a globally distributed suite via CDN (Level 5), your React components and Python entry points remain exactly the same. You can climb from a prototype to an enterprise-grade deployment without hitting a structural "cliff" that forces a rewrite.

The Two-Mode Strategy

We’ve implemented two complementary modes that work together:

  1. Build-Time (Monorepo/Bench): Our Vite plugin scans your workspace and creates a single optimized bundle. This is the "Indie" experience—fast, local, and simple.
  2. Runtime Module Federation (Enterprise): This is the game-changer for pip-installed apps. In production environments where Node.js/pnpm aren't available, the host shell fetches pre-built remotes via a Discovery API (GET /api/v1/frontend/remotes).

Why this matters for you:

  • No Node.js in Production: You can install wms-app or crm-app via PyPI, and the UI "just works" without needing a build step in your production container.
  • Independent Release Cycles: Teams can update a single module’s UI without rebuilding or redeploying the entire Desk.
  • The Python Source of Truth: We use Python entry points (framework_m.frontend) to drive UI discovery, keeping the backend and frontend perfectly in sync.

The "Modular Monolith" Advantage

While we support specialized headless engines like stock-keeper for high-throughput tasks, Business M (and any product built on Framework M) remains the unified hub. We aren't building a generic service aggregator; we are building a cohesive business suite that scales with you.


This post is a summary of the full technical deep-dive on the Framework M Blog.

What do you think? Does this approach to Module Federation solve the deployment hurdles you've faced with multi-package apps?

I'd love to hear your thoughts on the "Zero-Cliff" progression model.

2 Answers

Hi @Revant,

thank you for sharing.

My vision for modern deployment is using immutable containers that are reproducibly built on a backend system, so no developer tools (or even a full-fledged OS) are included in the container images.

Also, the containers should not require shared writable filesystem storage (e.g. ReadWriteMany in Kubernetes terms).

The question here is: how can we combine multiple FrameworkM apps with these prerequisities? And also: how can an existing system be reconfigured to include additional apps?

The nice thing about FrameworkM architecture, that it should allow deploying apps into separate containers / pods if the protocol implementations support this. While this definitely adds some resource usage overhead, I think that the security and reliability benefits by the enforced isolation of apps - and that they can only communicate through well defined interfaces - is worth it. I understand that this vision is not completely aligned with the modular-monolith approach that you propose, but I think it will become necessary to support multiple apps, that are developed by different teams, and should be kept separate. To clarify: I don't mean that every component of business-m should be packaged as a separate container. However, if there is a fictional HealthcareM solution, that is using business-m features, but also includes its own business logic and its own modules, developed by separate teams/companies ... etc., then I think it makes sense to support the deployment of this solution as a separate container.

This works well for the backend, but the question is (as you also point out) how can we handle it on the frontend.

Using Python wheels to package frontend components from the different apps and serving them at runtime is definitely an interesting approach, and should work well for internal applications, like the Desk.

However, in modern web development for public applications (e.g. a website or a webshop) the state of the art is to bundle, tree-shake and minimize the application, essentially executing a form of whole-program optimization. How would this work in your proposal?

Kind regards,
Gergely

My vision for modern deployment is using immutable containers that are reproducibly built on a backend system, so no developer tools (or even a full-fledged OS) are included in the container images.
Also, the containers should not require shared writable filesystem storage (e.g. ReadWriteMany in Kubernetes terms).

I am with you. Less shared state for us to worry. framework-m, framework-m-core and framework-m-standard are production ready packages, no dev dependencies here. The developer tooling and cli is in framework-m-studio.

If a team mandates devcontainer for developers then framework should support it. If team needs multi-os support without container then framework should support it. As far as possible framework should support broader use-cases unless the developer team diverge into known anti-pattern or ADR mentions explicit avoiding the case.

The question here is: how can we combine multiple FrameworkM apps with these prerequisities? And also: how can an existing system be reconfigured to include additional apps?

We are combining apps using python entry points. Check: https://www.frameworkm.dev/docs/developer/framework-entry-points. We can compose the required behaviour within an environment using set of apps. For python side all work flat. App is plugin and app is app. As long as the environment reads and acts on entrypoints the composition behaviour will be achieved.

For UI we need Main or Base App for shell and plugins/frontends for modules (mfe) loaded in shell. For now we have main app with sidebar and plugins add to sidebar. We are working with a UI/UX team to give us a better app composition UX. This means frontend is not flat like python, it needs a base shell registered in entrypoint and then frontend start showing in base shell.

(Anyone with UI/UX expertise can suggest if there are better alternatives)

What I mean by app vs plugin check here: https://gitlab.com/castlecraft/business-m/-/blob/main/docs/milestones/apps/business-m/modules/README.md?ref_type=heads#philosophy-base-app-vs-plugin

The nice thing about FrameworkM architecture, that it should allow deploying apps into separate containers / pods if the protocol implementations support this. While this definitely adds some resource usage overhead, I think that the security and reliability benefits by the enforced isolation of apps - and that they can only communicate through well defined interfaces - is worth it. I understand that this vision is not completely aligned with the modular-monolith approach that you propose, but I think it will become necessary to support multiple apps, that are developed by different teams, and should be kept separate. To clarify: I don't mean that every component of business-m should be packaged as a separate container. However, if there is a fictional HealthcareM solution, that is using business-m features, but also includes its own business logic and its own modules, developed by separate teams/companies ... etc., then I think it makes sense to support the deployment of this solution as a separate container.

Yes in theory apps can be deployed in separate containers if designed that way. Right now business-m is separated by bounded context of libs and events to be used to mutate data, it is designed as modular monolith with clear spearation. It can be progressively swapped and decomposed into microservice.

My opinion on microservice is bit different, Health care itself will not be a microservice for me. A core piece which is independent, decoupled, micro yet complete on its own is microservice for me. Health care may be set of microservices. I choose stock-keeper and book-keeper as microservice for independent scalability as those are the choking points for most ERPs. Rest of the things can be modular monolith, if somethings doesn't fit in monolith we decompose it into microservice as we evolve.

I will use Framework M to build modular monoliths first. Monoliths are easy for most things in the world. Module from the monolith can be swapped/decomposed into microservice when needed. My experience about these swaps; they should be paradigm shifts and not code copy into microservice.

What type of problem will decide the split into microservice:

  • In case of team issues, deployment separation is required for code isolation and ease of team work.
  • In case of service slows down, need high throughput specialized service deployed to overcoming bottlenecks.
  • Anything else, we solve that!

This works well for the backend, but the question is (as you also point out) how can we handle it on the frontend.

Microfrontends is how we are trying to handle it. The term is generally used along with microservices. We are using the same module federation for shell+mfe(s) composition and do it at runtime using python entrypoints instead of "traditional" microservices discovering remoteEntry.js using configs. In future entrypoints can discover remoteEntry.js from "microservice" as well.

We have the base app that registers shell entrypoint. We have apps/libs that register frontend entrypoint. base app's boot-rendering composes ui using the registered frontends and shell app.
Sidebar (or plugin configurable ui) loads plugin components and routes.

Locally same plugins discover, build and serve react frontend for development. On build, the main app builds shell, plugins build frontend. Each app packages its own assets in its own wheel.

Using Python wheels to package frontend components from the different apps and serving them at runtime is definitely an interesting approach, and should work well for internal applications, like the Desk.

Ideally what is packaged in wheel needs to have optimized bundle size. That is what is happening right now. pnpm commands will show warning if sizes increase.

If at all you have media, videos or something large in your builds or your builds are small yet you need to serve static assets from nginx or CDN, then that is configurable through config/env var.

If you manage to optimize and package the built dist js/css into python wheel and keep size under 1 mb then you get a single unit package that is versioned and can be used to extract assets send to reverse proxy/gateway or CDN progressively.

If you still feel the size should not go in python wheel, directly push to cdn or nginx image and use that instead.

However, in modern web development for public applications (e.g. a website or a webshop) the state of the art is to bundle, tree-shake and minimize the application, essentially executing a form of whole-program optimization. How would this work in your proposal?

We need internal desk as priority for building business suite. Internal in not really internal considering vendors, customers, employees involved.

Website / CMS is app/plugin we can build separate app for this with something like https://puckeditor.com for frontend and Framework M for backend.

For Webshop (due to the window shopping and comparison chart scenarios) high scale design needs to be in place from day 1. Or else we land up with basic crud listing that takes down database when people start viewing, comparaing and searching things at once. This is another set of plugins that will enable webshop, stripe-payments, subscriptions, memberships, etc for backend and few public routes

For webshop I prefer to build it using some ready frontend similar to refine.dev that we chose for internal desk, it will already have lot of things figured out. No need for shoppers to switch to desk from there. Only the back-office / internal ui that configures and manages the webshop will be standard @framework-m/desk based ui i.e refine.dev ui.

Edit:

Related