Some thoughts on your approach:
I want to have some important application data (like the route name) in each application manifest and having the host application read them.
Why does a child app have to know about its router configuration in the manifest.json
? Your host application is responsible for orchestrating all your children apps. It is the one, which should know the mapping of routes to the specific child apps.
To go a bit more into detail: There is probably a reason, why you have the child apps isolated in the first place - they are self-sufficient and independent from each other and the host. E.g. imagine, you could want to reuse one of the apps and run them as a stand-alone app. That is also the reason, why it is not ideal to go the other way to let the children register them selves in the host. Because this would enforce tighter coupling between the child and host - the child would have to know, that it is used in some bigger application.
Child app
Concerning the child app, it can just export some runnable callback, which will be invoked by the host, if the route matches. In your case with React, it could be sufficient to export a ReactElement
which will be rendered by the host. This entry point also could have been created in form of a bundle process with Webpack.
// as ReactElement
export const FooChildElement = <div>I am Foo child</div>
// as Component
export const FooChildComp = () => <div>I am Foo child</div>
Orchestration of children apps
I would like to avoid having to maintain a list of the current apps in the Host so the code it's as reusable as possible.
What you could do is create a separate orchestration layer in addition to your host layer which knows all children applications, and the router configuration. It could provide a map route -> Runnable child app entry point
. Host reads that map and injects it into its router, effectively having been freed up from knowing the list of current apps used. Nonetheless, it still has some transitive dependency/import
on the children apps in order to run them. You should ask yourself, if its necessarily a requirement to separate host from the orchestration. As you said, you could create the imports dynamically in your build process (Webpack), but why not just hard wire this relation at one specific place in your project as a plain JavaScript object?
export const routeToChildMapping = {
"this/is/a/foo/route": {
name: "foo",
entry: FooChildElement
},
"this/is/a/bar/route" : { ... }
}
Host
With this, the host just knows about the exported mapping, runs the app if the route matches, does some error handling and other envelope tasks.
const Router = ({route}) => routeToChildMapping[route].entry
// here getUrlSomewhere simplified
const MyHostApp = () => <Router route={getUrlSomewhere()}/>
I tried to simplify the big picture and obey to KISS
here, as its good to not overcomplicate things at the start. Of course, thats highly dependent on you build environment and project. So feel free to adjust that concept to your needs/requirements!