Dependency Inversion Container

Information

Dependency Inversion Container is a Facade between executables and internal modules of an application. It is a step of a runtime when implementations of interfaces are selected based on configuration of application. It happens before launching real processing.

This step allows for feature switching without modifying the code and rebuilding it.

For more information about Dependency Injection see Dependency Inversion Principle.

Example

Set of components used in executable:

type Set struct {
  Service domain.Service
  DebugRepo func() ([]byte, error)
}

Executable that uses Set of components to create a web server:

func main() {
  cfg := ...

  set := components.Build(cfg)

  ...

  router.POST("/api/v1/service", set.Service.HttpHandler)
  router.POST("/debug/get_all", asHttpHandler(set.DebugRepo))

  ...
}

Implementation of the container:

func Build(cfg Config) Set {
  ...

  postgresRepo := postgres.NewRepository(...)
  ...

  var searcher FullTextSearcher

  switch cfg.Searcher {
    case: "elasticsearch":
      searcher = elastic.NewSearcher(...)
    case: "solr":
      searcher = solr.NewSearcher(...)

  service := serviceimplementation.NewService(searcher, repository)

  return Set{
    Service: service,
    DebugRepo: postgresRepo.GetAll,
  }
}

Here we can see that the implementation of the searcher is selected based on the configuration of the application, we can:

  1. do AB tests,
  2. easily turn off features that depend on unstable backing services during outages,
  3. rollback to non experimental and more stable implementation,
  4. deploy application for different business clients with different backing services,
  5. run the application with mocks/logs for debugging or testing.

Resources