A superior simplicity dependency injection framework in under 60 lines of code. You are encouraged to fork it, copy-paste it to your source code and modify it as you please based on your requirements.
For full documentation checkout this blog post, including how this was built from ground up, the rationale and alternatives.
This is released under MIT license.
- Allows strong isolation of concerns
- Promotes decoupling of logic and modules from its variants
- Facilitates unique registries for different use cases
- Allows overriding dependencies in local scopes, eliminating global side effects
- Use with any primitive, struct or function values to be registered as a dependency
- Resolves dependencies until the root registry in the chain and fails gracefully.
- Unit tests with full coverage
- Basic gin server implementation for usage example
// Imagine your code needs access to certain configs at runtime
// You could hard-code it or pass it via a function
// But things quickly get messy if you need variants or need to check different behaviors during testing
func LoadConfig() string {return "Production" }
func LoadTestConfig() string {return "Development" }
// You could write it the traditional way with if-blocks
func BusinessLogic() {
config := LoadConfig()
if isTesting {
config = LoadTestConfig()
}
// The business logic has nothing to do with whether this code is being run under test or production
// The unnecessary
// TODO: use the config.
}
Using DI, allows for better decoupling and isolation.
func BusinessLogic(reg *diydigo.Registry) {
config := reg.Resolve("config")
// TODO: use the config
print(config)
}
Notice that the BusinessLogic
is far simpler and does not care about how or what
produces the config.
reg := diydigo.NewRegistry()
reg.Provide("Config", func() (any, error) { return LoadConfig(), nil })
BusinessLogic(reg) // prints Production
// All modules share the same registry:
config1, _ := reg.Resolve("Config") // Production
// Now override locally for tests, this creates a new registry:
newReg = reg.Override("Config", func() (any, error) { return LoadTestConfig(), nil })
BusinessLogic(newReg) // prints Development
// config1 still is production
config1, _ := reg.Resolve("Config") // Production
// config2 in any part of the app is now the test config!
config2, _ := newReg.Resolve("Config") // Development