What is a DI Container?
Tags: programming, dependencyinversion, designpatterns
Published: 2024-07-05 14:54:14

In the last post in this series we learned about dependency injection. 

We saw that we needed to manually create an instance of the database connection class before creating an instance of the post routes class. Then, we passed the connection instance into the constructor of the posts object.

Imagine, though, if we could just create an instance of the post routes class, and the system itself would automatically know that the posts class depends on a database connection and create that instance for us 💡.

There you have it! A Dependency Injection Container is doing just that. 

A DI Container is responsible for creating instances of classes and managing their dependencies. Here’s a more detailed breakdown:

## Registering Services
First, you register your classes with the container and specify their dependencies. For example, you tell the container that PostRoutes depends on a DatabaseConnection.

## Resolving Services
When you ask the container to resolve a service (i.e., create an instance of a class), the container:

1. Creates an instance of the class.

2. Resolves all the dependencies that the class needs by creating instances of those dependencies as well.

This means that the container automatically creates and injects instances of all the required dependencies, simplifying the process of creating complex objects with multiple dependencies.

# The lifetime of instances.

You need to keep in mind how long an instance live before it gets renewed. We call this scope. 

The 3 most common scopes are `transient`, `singleton` and `request` or even called `scoped` in .NET. 

## Singleton
The singleton scope means that the DI Container creates one instance of the service and keeps that same instance throughout the program’s lifetime. Every time you resolve the service, you get the same instance. This is useful for services that maintain state or are expensive to create and should be shared across the entire application.

#####Example: Logging Service
A logging service that logs messages to the console can be a good use case for a singleton service. A logging service keeps the same state and stay consistent throughout the program.

## Transient
Transient scope means that the DI Container creates a new instance of the service each time it is resolved. This is useful for lightweight, stateless services where each operation should be independent of others.

#####Example: Unique ID Generation Service
A service that generates unique IDs might be a good use case for a transient service. Each request to this service should produce a new, unique value without retaining any state from previous requests.

## Scoped or Request
Scoped services last as long as the HttpContext lasts. This scope is particularly useful in web applications.

When working in the backend, you receive HTTP requests from clients. Each request has a context, known as the HttpContext, which lasts from when the client sends the request to the backend until the backend sends a response back to the client. During this period, multiple operations might occur, such as connecting to a database, fetching data to authorize the user, retrieving various resources, etc.

#####Example: Database Connection Service
For a database connection service, you want the same instance to be used throughout the entire HttpContext to avoid losing the connection during the request processing. This ensures that the database connection remains consistent and efficient throughout the lifecycle of the request.

#####Stay tuned because in the next chapter we will start building our own DI container in JavaScript.