vercel / next.js

The React Framework
https://nextjs.org
MIT License
124.08k stars 26.43k forks source link

Feasibility of micro frontends #6040

Closed janus-reith closed 5 years ago

janus-reith commented 5 years ago

Im evaluating wether nextjs would be suitable for a microservice approach, where the page is build from multiple separate components that could be deployed separately. I found different concepts that only render serser side static templates or create the content clientside.

I would like to have am isomorphic approach, preferable with nextjs. Did anyone implement such a solution, or what would be your opinion on the technical feasibility?

E.g. the content is build from three separate nextjs apps, each running on their own port or instance. One would serve as the main UI and have a navigation to allow seamless transition between routes.

I also wonder how they could be scoped, although I think styled-jsx would really offer benefits here due to its nature.

I'd also welcome different solutions for this, main requirements would be:

AlanPieczonka commented 5 years ago

Just out of curiosity, what would be the purpose of this approach?

janus-reith commented 5 years ago

The purpose would be to enable a vertical style of development - Different teams could individually develop and deploy the parts relevant to them without having to deploy the whole stack. in a specific case for a client that would be differencent teams taking care of different services, which should be presented to the customer In one seamless experience.

While this could certainly be implemented as a monolithic solution that just fetches data from different sources, we want to ensure future suitability for further growth of the company and its teams, as communication and arrangements get more complex.

tk-o commented 5 years ago

I just wonder why would you have multiple separate next.js apps?

I'd see one worker running next.js. You can already have multiple components on your pages (developed separately). Given the following package.json snippet (your next.js app worker):

{
  "dependencies": {
    "@alltheteams/navigation-box": "2.0.4",
    "@alltheteams/product-listing-box": "1.3.9",
    "@alltheteams/footer-box": "1.5.0"
  }
}

Now, let's presume the team working on @alltheteams/product-listing-box app deploys a new version of their code. The only thing that's needed is to run post-deployment action that would set the new version number in the next.js worker. I could be yarn upgrade @alltheteams/product-listing-box for example.

This is a simple scenario, however, I believe it could work quite well.

Any thoughts about this idea? 🙃

tk-o commented 5 years ago

I tested the approach I've mentioned above. It was an easy thing thanks to create-react-library. That lib helped a lot with building the mico React apps. Please let me know if the example would be needed ;)

marcodejongh commented 5 years ago

@kopacki Wouldn't those dependencies mean that the main app would have to be rebuild for every change?

The purpose would be to enable a vertical style of development - Different teams could individually develop and deploy the parts relevant to them without having to deploy the whole stack.

Independent deployment seems to be a requirement.

tk-o commented 5 years ago

Microfrontends are essentially reflecting microservices approach. If you update the dependency of some microservice, you have to deploy that microservice. The deployment of Next.js application worker could be done seamlessly, so in the end, once the team deploys a new version of their microfrontend, their work is done.

tk-o commented 5 years ago

After some thinking, I'd say that you could have an API that delivers components as stringified functions. These stringified functions could be run through eval() so you'd end up having a react component injected dynamically without a need to deploy it. This, however, would play well only for some presentational components (that not carry any assets like images).

revskill10 commented 5 years ago

I think it's possible ONLY if output of NextJS is not the whole HTML, but only a section of the page, like only a div fragment which contains the prebuild HTML output. I love the concept of micro-frontend as the original comment already said about benefits.

ranjanrajiv00 commented 5 years ago

Im evaluating wether nextjs would be suitable for a microservice approach, where the page is build from multiple separate components that could be deployed separately. I found different concepts that only render serser side static templates or create the content clientside.

I would like to have am isomorphic approach, preferable with nextjs. DId anyone implement such a solution, or what would be your opinion on the technical feasibility?

E.g. the content is build from three separate nextjs apps, each running on their own port or instance. One would serve as the main UI and have a navigation to allow seamless transition between routes.

I also wonder how they could be scoped, although I think styled-jsx would really offer benefits here due to its nature.

I'd also welcome different solutions for this, main requirements would be:

  • (isomorphic) React / SSR
  • Each part should be deployable separately.
  • Content should be composed into one page and not just sit on different routes.

I am also doing feasibility analysis on micro frontend approach. Currently we have monolith app developed using React, Redux, Next.js(SSR). So wanted to split into micro apps.

Independent deployment, seamless navigation and SSR is major functionality must be in micro frontend approach.

I am still in progress to finalize and implement, but my approaches are as below -

  1. Create micro apps (let say, Product, Cart, Payment, Order)
  2. Micro apps is in react ( no next.js)
  3. Customized webpack to generate UMD bundel
  4. Micro apps exposing express.js api to serve assests (umd bundel and css)
  5. Main app (app shell) will have responsibility of routing, composing micro apps.
  6. Main asynchronously load apps and assests to compose micro apps.

As of now, CSR and micro apps has implemented.

Yet to implement-

  1. SSR
  2. Routing
  3. Redux for apps state

Any further suggestion is most welcome, if it is feasible with next.js the it would be great.

janus-reith commented 5 years ago

@kopacki Yes there're several ways like the one you gave as example to compose the application out of different modules, requiring them to be deployed together.

A best-case scenario would be, if each separately deployable part would still run through a bundling and optimization process.

If we take the concept @kopacki gave and serve them stringified, the missing Link would be, how to let an instance of e.g. express use nextjs to stitch them together on the server.

@ranjanrajiv00 Yes I also came up with some ideas how to do this client side, but not a real solution on how to do this server side.

I think a general question here could be: Could a node server framework, like express make use of nextjs to render explicitly into a part/container of a whole page, and serve that to the client while maintaining getInitialProps features? (Just noticed, essentially this is what @revskill10 mentioned)

This would still leave open what would be the best way to share these components(like stringified functions) and also, if encapsulation would work properly, if multiple sources would be composed into one page.

tk-o commented 5 years ago

I've been working on the concept of having some components (like Design System library) imported as ordinary npm packages (so these would be compiled and optimised), and also having some functional components (template partials) imported as strings from a Template API.

There is a small demo of my idea. I want to have two APIs, one for content and the other one for templates. These APIs would provide all information required to render given page.

I will let you know once I have more detailed idea on how to implement that concept.

ranjanrajiv00 commented 5 years ago

After analyzing different approach, I implemented micro frontend as below (without next.js).

Created micro apps (Core, Product and Cart).

  1. All micro apps are independently developed and deployed
  2. Configured webpack to build UMD library
  3. Micro apps will expose two api endpoint implemented in express.js
    • /api/assests -> It will serve js & css
    • /api/render -> it will return SSR html

Created a AppShell (container app). Container app are responsible for below-

  1. Seamless Routing
  2. CSR & SSR (Hydration)
  3. Load micro apps asynchronously
  4. Composing micro apps

With this approach i am able to implement micro frontend as our requirement. But still would be interested to see if i can implement same with next.js (as we are using next.js in existing application). Otherwise we have to remove next.js from future plan (micro frontend journey)

janus-reith commented 5 years ago

@kopacki Your approach seems interesting, and appareantly it mostly boils down to using react-jsx-parser to parse the stringified components.

The example you gave is clientside, will try if that works with nextjs/ssr aswell. According to the issues in react-jsx-parser there seem to be some people using it serverside.

janus-reith commented 5 years ago

@ranjanrajiv00 Could you give more details on how these things work together? What I wonder:

3. Load micro apps asynchronously

What does that mean essentially? Are those fetched on the server and then served to the client together with your container app, or are they just rendered clientside and therefore not taking advantage of SSR?

Also, does /api/render just serve statically rendered html, or does it keep up any of reacts dynamic functionality? If the latter is the case, does it maintain its own scope?

ranjanrajiv00 commented 5 years ago

@janus-reith Micro app will generate UMD bundle and can be deployed independently. Container app will download bundle (micro app's UMD).

/api/render will render react component using renderToString() and return to container app for SSR. Container app will do all composition and SSR and again it will be hydrated at client using ReactDOM.hydrate.

revskill10 commented 5 years ago

My suggestion for general case is: Ability to provide a React Component as a service. That means, we need to return React Component as a JSON response, and ability to reconstruct it both on NodeJS and browser. So in case of NextJS, we do it in getInitialProps. Actually, NextJS is the perfect shell to compose remote React Components universally.

ranjanrajiv00 commented 5 years ago

Any example/sample code for micro frontends with next.js approach.

ranjanrajiv00 commented 5 years ago

@janus-reith and all, could you take a look into my approach. https://github.com/ranjanrajiv00/micro-frontends

Your feedback/input would give me to make it better.

AlexGilleran commented 5 years ago

I'm looking into this at the moment. As it stands you can't run two instances of next.js in the same browser because they collide on window.__NEXT_DATA__, window.__NEXT_P and the <div id="__next"> element.

I did a quick test of this - an outer next js app using the default next js and fetching the output of an inner next js app which was npm linked to another version of next.js where all the above global variables were suffixed with 2 (i.e. __NEXT_DATA__2 etc) using find-and-replace. Worked fine. Without doing this the best I could do was grab the HTML from the inner app, but the javascript would never work.

Would it be feasible to namespace those globals above? I'm not sure exactly how, maybe we could specify a suffix in the config or something?

janus-reith commented 5 years ago

@janus-reith and all, could you take a look into my approach. https://github.com/ranjanrajiv00/micro-frontends

Your feedback/input would give me to make it better.

Sure, will take a look :)

janus-reith commented 5 years ago

@AlexGilleran Yes, this were the concern I had regarding the scope of each next instance. Being able to define a custom name/pre-/suffix for them would be helpful, I also couldn't find an existing config option for that yet.

brendonco commented 5 years ago

I would love to see this happening for microfrontend. Legacy code(monolith) can be splitted into different docker container and independent to each other. Similar technique develop by Taylorjs.

timneutkens commented 5 years ago

Trulia.com is moving to Next.js using zones (https://nextjs.org/docs/advanced-features/multi-zones)

ZEIT day talk: https://www.youtube.com/watch?v=f7BMSVhZan0

Video they did talking about it: https://www.youtube.com/watch?v=4RmAl6HUsW0

Article series on their blog: https://www.trulia.com/blog/tech/paying-off-tech-debt/ https://www.trulia.com/blog/tech/islands-and-the-application-shell/ https://www.trulia.com/blog/tech/graphql-one-endpoint-to-rule-them-all/

More general article about the "why": https://www.trulia.com/blog/tech/paying-down-tech-debt/

At this point in time we're not planning on allowing running multiple Next.js apps in 1 page, as it adds a really big amount of complexity into how this have to work and how they're hydrated etc. Maybe we'll support it in the future.

Zones allows you to gradually migrate to Next.js and independently deploy Next.js apps.

We'll be focusing on improving the zones features 🔜

kirankalyan5 commented 5 years ago

I'm not sure If I understood Zones properly, Does it means to enable or use Zones approach the entire infra (i.e each micro-app will be a zone and the app shell/ container of these micro-apps should also be built using Next JS ).

In my requirement, I will be responsible for building a micro-app (I would love to use Next JS) and the other micro apps teams and the core/main(one who is responsible for stitching the app ) aren't using Next JS. As a contract from each Micro App, the core APP would expect 2 endpoints

  1. An SSR HTML
  2. static asset (JS, CSS etc)

I'm not sure will Next JS serve my purpose. Would appreciate your advice on if I can or cannot use Next JS?

CC @timneutkens @ranjanrajiv00 @janus-reith

Thanks in advance

aadamsx commented 5 years ago

@timneutkens I reviewed zones and the posts and youtube videos from Trulia. Trulia's method sounds great, but I don't see how zones does builds the architecutre they're talking about. For example, the base Next app contains shared responsibilities like security and the top and bottom nav bars. The other Next apps are build into this app and use the base app's security and navigations -- Next apps with Next apps.

Yet in the zones examples they don't and can't share anything. They are separate apps on different URLs "glued" together with a proxy. I don't see how you get from Next Zones to what they describe.

Anyhow I could just use the proxy to glue together any app, including "create-react-apps". What I thought was different/exciting was having "full stack" micro services that included the front end (normally it's only micro-service APIs), and having a centralize next app "container" that "glued" them all in one.

elviocb commented 4 years ago

@timneutkens @aadamsx Have you discovered anything else about how trulia implemented their “app shell”? The current Nextjs docs describe zones as independent apps glued by a proxy. I’m facing the same issue. I need to implement a new architecture to an ERP App, its a huge one. The app contains 10 modules, thats a perfect case for micro frontends and NextJs is the best solution so far.

msreekm commented 4 years ago

interested in seeing an example of this much talked "app shell"

Jordan-Gilliam commented 4 years ago

I would love to continue the conversation and contribute. The current inability to stitch micro frontends is a primary deterrent from adding Next to our stack.

I understand the core teams stance, but this seems like a bottleneck when building very large scale, dynamic, next js applications.

Jordan-Gilliam commented 4 years ago

This is Incredible! https://github.com/webpack/webpack/issues/10352 👀

ScriptedAlchemy commented 4 years ago

I am actively working on this: ☝️ it'll be part of Webpack 5 core

I'll have to backport it to WP4 if there's enough motivation.

janus-reith commented 4 years ago

@ScriptedAlchemy Wow, this is huge and really seems to tackle the issue at its root. I'm impressed!

Also thanks for the link @Jordan-Gilliam

Jordan-Gilliam commented 4 years ago

anyone interested in building a ssr framework with micro frontend support? https://twitter.com/rauchg/status/1229856712921759744?s=21

ScriptedAlchemy commented 4 years ago

@janus-reith Thank you. I've been really frustrated that nobody actually solved this. Been building MFE stacks since 2015 and have thoroughly hated the infra and appalling amount of manual labor required. It's about time this issue is put to bed once and for all.

Due to having literally written this into Webpacks core, ill be spinning up a new monorepo of various tools, routers, and other such things to offer more robust capability.

There's usually some concern around the concepts of automatic sharing or module export merging. But its already working for me so I'm struggling to see why I shouldn't offer it again as a third-party enhancement to what I'm working on.

These ideas are experimental and Id much prefer a stable initial release with fewer advanced use cases built-in. Keeps its simple to write test cases for.

@Jordan-Gilliam While unrelated to Webpack Federation - I've figured out how to support Suspense SSR without the current workarounds I actually co-authored (all code-splitting SSR systems are based on my original workaround react-universal-component)

Suspense SSR, at least at this stage is quite straightforward. But I need to create a ServerModuleFactory and ServerSuspenseModule to support it. It's next on the list though and a much easier problem to solve compared to Webpack federation. This tool will be a separate project to FedereationPlugin, obviously. Over at webpack, we are working on support for multiple target entry points which let one build output both server and client runtimes - a perfect opportunity for the introduction of ServerModuleFactory and ServerSuspenseModule

@msreekm Regarding the App Shell concept - I designed one specifically for Webpack Federation. The core concept is that I should be able to federate pages or drag and drop apps/pages into other next.js servers built with my App Shell

So far it works like a charm & in production. I've been able to consolidate and distribute components, pages, and apps with 0 refactor time and no binding whatsoever to _app or _document

I'm more than willing to work with anyone who shares my views or motives. Anything and everything federation related will be published as open-source software under an official project ill put together in the next few weeks.

Let's build the future we want ❤️

theoludwig commented 4 years ago

@ScriptedAlchemy Any news about that ? I really want to accomplish micro-frontends with Next.js but have no idea how. And yeah "Let's build the future we want ❤️" absolutely!

ScriptedAlchemy commented 4 years ago

I am working with the next.js team. There are three branches in WIP status. After those make it to a merge, ill pull the project down again and begin opening PRs for anything else, as well as start testing this out. From my initial PR is will be possible. Ill just need to add some documentation to Next or a medium post about how to enable it.

It will come, guaranteed. As a workaround, i have a WP4 shim that allows next to consume federated code. However, there's limitations. I do use the shim in production and it powers AB tests and Analytics.

msreekm commented 4 years ago

@ScriptedAlchemy i am excited for Webpack Federation technology ! Waiting to see what you are building .

ScriptedAlchemy commented 4 years ago

Getting closer. Attempted to livestream today but had some issues.

msreekm commented 4 years ago

is next.js getting webpack federation support soon? @ScriptedAlchemy .thanks for the module federation examples on your channel.

ScriptedAlchemy commented 4 years ago

To be determined. Module Federation isn't a primary concern of Next, However if Next runs on Webpack 5 we should be able to make a plan with the configurations. I’m in comms with some of the internal members, internal support: unknown, compatibility: likely

Jordan-Gilliam commented 4 years ago

One of the biggest issues with module federation and Next seems to be trying to force Next.js into something that it was never designed to be. Next.js is optimized for Vercels platform.

Module Federation changes the rules. We need to start from a lower design level than Next. I'm beginning to organize these thoughts in a new project with the goal of leveraging webpack to begin creating abstractions that treat [MFE, Federated Modules, Multi-Threading, Serverless, NoServer, and Edge Computing] as first class citizens with 0 cloud deps.

Please note this project extremely un-refined and is the result of 1 coding session. @ScriptedAlchemy https://github.com/Jordan-Gilliam/konkres

ScriptedAlchemy commented 4 years ago

Next.js is a part of a commercial offering. We are asking for prioritization of experimental software in a commercial product 📦 That product has its own delivery roadmap that we are, effectively, disrupting.

Based on my own inspections of the source. Full module-federation support would require one async boundary be installed within the next server and next client entrypoints, before React is initially imported.

If you follow my work closely, you'll know I am already using Module Federation on this platform and have moved on to upgrading the other third-party plugins that stand in my way of a production deployment at my own company. Its not perfect based on the missing async boundary, but it lets me move forward and I can manage the workaround for the time being.

vonwao commented 3 years ago

Hi, anyone following this thread and want to discuss this further.

This seems like a mess to me! Quite complex. Are there any solutions that can work now, i.e. that don't depend on this new webpack feature?

I don't care very much about SSR of my "subapps". I would be happy to use next.js to render the app shell, and then maybe use import-maps to load the subapps inside the app shell. My main requirement is to have a the subapps CI/build/deploy separate from the app shell.

ScriptedAlchemy commented 3 years ago

Module Federation is kinda the only solution I'm aware of with next.js. Other than loading separate apps entirely.

If you don't care about SSR, Module federation isn't very complicated to implement client side, I've got it in production at the moment without issue

Jordan-Gilliam commented 3 years ago

@vonwao The api for Module Federation seems clear considering the complexity that it manages. Versioning and name-spacing are the main concerns so far in our implementation (we aren't using nextjs). Effective E2E tests + Federated Dashboard will also definitely help quell governance anxiety.

"My main requirement is to have a the subapps CI/build/deploy separate from the app shell".

Do you have a shared k8s volume in your cluster?

Are you aiming for something like Trulia's Architecture?

mlstubblefield commented 3 years ago

Super excited about this initiative.

We're looking at moving towards using vercel/nextjs. My company is also requiring the use of microfront ends to further benefit from decoupling our application(s), so it's really cool to see the prerequisite pieces start to fall into place. https://www.thoughtworks.com/radar/techniques/micro-frontends

I notice this issue has been closed. Is there a related issue that is open that would be worth following in addition to this one, especially as webpack 5 goes into release candidate?

I want to say I read that @ScriptedAlchemy has a pull request to make things come together rather nicely?

@timneutkens?

ScriptedAlchemy commented 3 years ago

Next supports webpack 5 as of 9.5 - the webpack RC was just released so if you use the RC you'll need the canary version of next.

There's still issues beyond supporting webpack 5 that's related to how next is built.

I've got next and webpack 5 happily in production. Sharing vendors requires some workaround in next 9.

Next 10 should have async support internally which should enable module federation, without workarounds. I'm not sure if vercel plans to use MF inside of next itself (each page is an entrypoint, they could treat each page as a remote - having the host use its own remote) But I believe we will have MF support 🎊

czterystaczwarty commented 3 years ago

@ScriptedAlchemy existing news.

I wonder if it is possible for each remote entry to have a page set (eg. /pricing, /solutions) and the host would only know about their prefix. (/product1 /product2).

With the team, I develop such a project using CRA, React-router in host and entry, single-spa and a lot of vanilla code to async loading with edge cases. Without SSR of course. Maintenance is a nightmare.

ScriptedAlchemy commented 3 years ago

Yeah it's possible to have MF pages. You'd likely use the catch all route and have it fed to MF, see if there's a mapping available, if not then 404.

I'm busy upgrading all company repos to WP5 stable and NextJS 955. While you won't be able to use sharing without workarounds. (You can't share react, thus loading multiple copies)

Soon as I've got my large repos in production - I'll start working on federated page routing and will make a repo or something. I'll likely use react and react dom as externals until next is async.

Overall, client side spa style routing between apps is the easiest.

You can also replace the next.js router with react router or use that in the catch all as a way to route stuff easier.

czterystaczwarty commented 3 years ago

Overall, client side spa style routing between apps is the easiest.

Of course it is. But I want to have SSR with fetching data, clean architecture, low cost maintenance and that there will be peace in the world. ;)

ScriptedAlchemy commented 3 years ago

I would not ssr pages from other locations. SSRs would happen on another server. Client side, I'll pull chunks for routing. SSR, my infrastructure points my request to the right lambda. Each remote should be capable of rendering itself. I use a decentralized AppShell. Each host/remote has its own shell. I don't need to ssr pages that belong to something else. I'd just render on the server that handles that route.

If you did want SSRd pages, a use advanced config and make react shared but import false in advanced api. Then you'd need to try shared and eager in the main host. But you might not even be able to share it in next. So be prepared to set a window global for all remotes to get react. Or manually initialize react into their share scope from next.