IoC and Container
Context
Inversify is a lightweight inversion of control (IoC) container.
IoC Container
IoC container uses a class constructor to identify and inject its dependencies.
What is Dependency
When you need another piece of code to run the current code.
A
relies onB
to work
class A
uses methods from class B
= class A
is dependent of class B
class A needs to have instance or copy of B inside of A to work
What is Common dependency
- User model needs to talk to a database
- Database used in
User
model isMySQL
persist
method is called to store data into MySQL
- Database used in
User
model needs Database
's persist
method.
// before inversion public class User { MySqlDatabase database; public User() { database = new MySqlDatabase(); } public void() add(String data) { database.persist(data); } }
Inversion of Control (IoC
)
IoC is flipping of dependency.
- It's inverse in regards to procedural programming flow of control.
Instead of defining the type of database
used in the User class
directly as concrete implementation, you instantiate the User
class and pass a Database
type to it.
- More precisely - Database type to be implemented as part of User.
// previously user = container.new User(); public class User { database = MySqlDatabase(); } // dependency inversion user = container.new User(MySql()); public class User { this.database = database }
What did this inversion accomplish
Allows more abstraction over concrete implementation
- loosely coupled architecture, plug-ability, flexibility
- allows for handling of things like changing parameter or database types from specific implementation
Benefit - Not solidifying concrete implementation choice now which will become harder to rip out or change when the codebase grows large and requirements change
Injection
Injection is when you pass something into the code instead of reference/using it directly.
- you inject a code you need into another code
Passing the concrete decision as a parameter
Remove direct dependencies between components by injecting their dependencies from external sources
// Using Constructor class UserService { constructor(database) { this.database = database; } // ... } const database = new Database(); const userService = new UserService(database);
Why does IoC
use interfaces
?
The concrete implementations of the interfaces are not meant to be exported from the projects, which means they are not available to clients, and you have to use the interface.
This decouples our function implementations from the interfaces, and allows us to maintain the functions and different implementations without affecting the interface footprint in the rest of the system.
Callsites become a million times easier to maintain, and teams can refactor their internal project code without worrying about where and how the concrete function implementations are defined.
Can also use setter
Illustration skipped as it is not a recommended pattern.