مهدی خانزادی
۲۷ مرداد ۱۴۰۵
Large software is built from small, independent parts — and those parts need each other. This article explains what a dependency is, why injecting it from the outside beats constructing it inline, and compares the four main DI tools in Go: Dig, Wire, Fx, and Container.
Large software is made of smaller, more independent parts. To build a complex system, we split it into pieces, give each piece one clear responsibility, and then assemble them together.
Each of these pieces usually can't do everything on its own — to fulfill its responsibility, it may need other parts of the software.
Here, Manager depends on Database.
This approach has problems:
database.New has requirements of its own, New has to declare them tooDependency injection means passing the dependency into the code from the outside.
We can also add a method to replace the dependency:
Container is a lightweight library for dependency injection and Inversion of Control (IoC) in Go.
It uses the concept of binding to connect an abstraction (interface) to its real implementation.
Container supports several kinds of dependency management:
Dig is one of the most popular dependency injection libraries in Go, developed by Uber.
It uses reflection to manage and inject dependencies at runtime.
Dig offers developers a simple API for declaring dependencies, and by analyzing the dependency graph it automatically resolves what each part needs.
Wire is a lightweight dependency injection library for Go, developed by Google.
Unlike Dig, which injects dependencies via reflection at runtime, Wire uses code generation — it produces the wiring code at compile time.
Wire's main focus is simplicity, performance, and compile-time safety.
Fx is an open-source application framework for Go, developed by Uber.
Beyond dependency injection, it provides extensive lifecycle management for application components.
Fx uses Dig under the hood, but adds more — startup and shutdown management, and organization of application modules.
| Feature | Dig | Wire | Fx | Container |
|---|---|---|---|---|
| Developer | Uber | Uber | Danceable | |
| DI approach | Runtime DI via reflection | Compile-time DI via code generation | Runtime DI built on Dig | Runtime DI via a container |
| When dependencies resolve | Runtime | Compile-time | Runtime | Runtime |
| Uses reflection | ✓ Yes | ✗ No | ✓ Yes | ✓ Yes |
| Compile-time safety | Limited | Very high | Limited | Limited |
| Ease of use | Moderate | High | Moderate | High |
| Flexibility | High | Moderate | Very high | High |
| Lifecycle management | Limited | ✗ No | Very powerful | ✓ Yes |
| Scope support | ✓ Yes | ✗ No | ✓ Yes | ✓ Yes |
| Runtime configuration | High | Limited | High | High |
| Code generation | ✗ No | ✓ Yes | ✗ No | ✗ No |
| Manual configuration required | Moderate | Low to moderate | Moderate | Low |
| Good for small projects | ✓ Yes | Excellent | Usually overkill | Excellent |
| Good for large projects | ✓ Yes | ✓ Yes | ✓ Yes | ✓ Yes |
| Learning curve | Moderate | Low | High | Low to moderate |
| Runtime performance | Good | Very good | Good | Good |
| Good for microservices | ✓ Yes | ✓ Yes | ✓ Yes | ✓ Yes |