InversifyJS icon indicating copy to clipboard operation
InversifyJS copied to clipboard

Sharing a single container among multiple packages

Open wheaney opened this issue 4 years ago • 3 comments

I'm looking for best practices or recommendations for my usage of Inversify. I'm defining a few layers of packages that all need to share a container instance. Each layer provides its own ContainerModule and adds it to the container before handing that off to the next consumer.

So the package structure looks something like: Application framework package

  • Defines the container instance (new Container()) and exports it
  • Defines its own ContainerModule, loads it to the container, and exports it (in case a consumer wants to unload the module)
    • Binds its own service implementations, used by the framework and consuming packages
  • Creates and exports a couple helper functions that utilize the container (e.g. lazyInject, useInjection React hook)
  • Utilizes dependencies from all 3 layers in its code (may depend on some bindings that will be satisfied in consuming packages)

Library package providing shared implementations of interfaces and commonly used components that work with the framework

  • Imports the container instance from the framework (and re-exports it)
  • Defines its own ContainerModule, loads it to the container, and exports it
    • Binds its own service implementations, some may satisfy needs of the framework code, this package, and consuming packages
  • Imports, re-exports, and utilizes the helper functions
  • Utilizes dependencies from all 3 layers in its code (may depend on some bindings that will be satisfied in consuming packages)

Application package that consumes both of the above and ties everything together with custom code into a deployable application

  • Imports the container instance
  • Defines its own ContainerModule, loads it to the container
    • Binds its own service implementations, some may satisfy needs of the previous 2 layers and this package
  • Imports and utilizes the helper functions
  • Utilizes dependencies from all 3 layers in its code

This all works but is a little fragile and can be broken by the fact that imports at each layer may cause NodeJS to re-interpret the container creation code that's happening at the framework layer; in fact right now I'm seeing my framework ContainerModule being loaded multiple times if I add debug logs, even though at the moment it's all still working (I've had to tighten up imports to get it to this state, though).

In my opinion it would be best if the top-most package (the application package that consumes the other 2) defined the container and loaded the ContainerModules from the packages it consumes, but the framework code needs direct access to the container instance in order to initialize lazyInject and useInjection so that doesn't seem feasible.

Any ideas on how I can clean this up?

wheaney avatar Apr 21 '21 20:04 wheaney

One option that came up with as I wrote this is to follow the solution described in the last paragraph, essentially the top-most consumer package would be responsible for defining and owning the container, and get rid of my usage of lazyInject and useInjection from all other packages so they never need access to the container instance. I'm using these mainly in defining React class and functional components that can't use @inject. This would force me to do dependency injection via constructor or function params and define wrapper classes/functions for all of them in the consuming package, which is really tedious and ugly in my mind and defeats some of the benefits of Inversify, but gets me closer to the better container ownership model.

wheaney avatar Apr 21 '21 20:04 wheaney

The best solution I can come up with to allow for moving container instantiation/ownership out of the libraries and into the consuming application would be to move all React components into a service/factory that's provided by the ContainerModule. This would allow these components to be initialized from a context where they have access to the container and thus lazyInject/useInjection. I could see InversifyJS providing tools to make this pattern simpler.

wheaney avatar Apr 22 '21 18:04 wheaney

Hi @wheaney I think your last approach would be the best one. Depending on the bundle process, @lazyInjection may not be a good idea (see this issue for more details)

notaphplover avatar Apr 25 '21 08:04 notaphplover