typedi icon indicating copy to clipboard operation
typedi copied to clipboard

feature: container inheritance rework

Open NoNameProvided opened this issue 4 years ago • 0 comments

Description

This task aims at rethinking and rewriting how container inheritance and dependency lookup works between containers. This task should include rework or implementation of the following features

  • allowing to set services globally, for a specific scope or as transient (we already support these)
  • allow easily overwriting some of the dependencies of a given class (ref #186)
    • either making it easy to create a container with parent lookup (preferred)
    • or allowing specifying custom overrides when calling container.get()
  • allow containers to lookup missing dependencies in their parents
  • allow containers to inherit dependency metadata from their parents
  • allow containers to dispose of themselves, destroying all locally registered service

Currently a container can be requested with containerInstance.of(containerId) function. This returns a new container if no container exists with the given ID or returns the existing one. There are several problems with this solution:

  • caller has no idea if the container existed before or not
  • there is no way to specify how a container should inherit services from a parent container
  • currently, there is no lookup in parent containers besides checking the default container for global services

Proposed solution

The new implementation should:

  • allow specifying whether an error should be raised when a container exists with given ID
  • allow parent-child relations, where a dependency is looked up in the parent container if it's doesn't exist in the child
    • this should not be limited to a single level, but should recursively look up, until the dependency is found or a container specifies a lookup strategy that doesn't allow further lookups in the parent container
  • allow cloning the registered metadata from the parent container
    • either with values, meaning the resolved value of ExampleClass service will be the same for both containers
      • technical note: this means the metadata is shared and we need to store which container uses which
      • technical note: what happens if one container resets it's value?
    • or with definition only, meaning a different instance will be created for the child container
      • technical note: how inherit value only dependencies?

Proposed API

// default container instance, always exists, cannot be destroyed
import { Container } from 'typedi';

Container.of('my-new-container', {

  /**
   * What should happen if an existing Container is found with the given ID.
   *  - `throw` means TypeDI will raise an error
   *  - `overwrite` - the previous container is disposed and a new container is created
   *  - `returnExisting` - returns the existing container if the same parameters where specified 
   */
  onConflict: 'throw' | 'overwrite' | 'returnExisting',

  /**
   * Controls what should happen when a type is requested what doesn't exists in the current container.
   *  - 'allow' means the parent container can be checked for the given type, if found the parent container will hold the instance
   *  - 'localOnly' - means types are checked locally only and if no type found a ServiceNotFoundError is thrown 
   */
  lookupStrategy: 'allow' | 'localOnly',

  /**
   * Enables or disables the lookup for global services. By default every requested type is first checked if it's 
   * a global (singleton) service in the default container. This check bypasses the lookup strategy, so if this behavior 
   * is not desired it can be disabled via this flag.
   */
  allowGlobalLookup: boolean;

  /**
   * Controls how the child container inherits the service definitions from it's parent
   *  - `none` means no metadata is inherited
   *  - `definitionOnly` means metadata is inherited and if an instance already exists it's ignored
   *  - `definitionWithValues` means metadata and service instance both inherited, when the parent container is disposed
   *     the instance in this container is preserved
   */
  inheritanceStrategy: 'none' | 'definitionOnly' | 'definitionWithValues',
}

NoNameProvided avatar Feb 21 '21 16:02 NoNameProvided