https://github.com/quangtung97/svloc
A DI Container alternative for Go
https://github.com/quangtung97/svloc
dependency-injection go golang static-typing
Last synced: about 1 month ago
JSON representation
A DI Container alternative for Go
- Host: GitHub
- URL: https://github.com/quangtung97/svloc
- Owner: QuangTung97
- License: mit
- Created: 2023-10-13T02:38:47.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2024-02-12T14:02:51.000Z (over 1 year ago)
- Last Synced: 2024-04-16T00:16:56.411Z (about 1 year ago)
- Topics: dependency-injection, go, golang, static-typing
- Language: Go
- Homepage: https://pkg.go.dev/github.com/QuangTung97/svloc
- Size: 86.9 KB
- Stars: 4
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# A Library for Dependency Injection in Go
[](https://github.com/QuangTung97/svloc/actions/workflows/go.yml)
[](https://coveralls.io/github/QuangTung97/svloc?branch=master)## Why this library?
* It is simpler than [uber/fx](https://github.com/uber-go/fx), yet still powerful
* Static typing & without using reflection
* Easy to use and easy to replace objects for unit & integration testing (by using **Override**, one can replace any part of the **default** dependency graph)
* Easy to read & navigate with IDEs
* Safe to use in multiple goroutines
* Very easy-to-read stacktraces when something wrong happens
* Provide **default** wiring through the **Register** method, or no default wiring through **RegisterEmpty**#### This library does NOT actually use the Service Locator pattern
### Limitations
* Runtime wiring of objects
* Deep stack calls
* ``Override`` functions only work outside of the ``anonymous functions`` in the ``Register`` calls
## Installtion```bash
go get github.com/QuangTung97/[email protected]
```## Examples
Assume ``RepoImpl`` implements interface ``Repo``:
```go
package maintype Repo interface {
GetUser() string
}type RepoImpl struct {
name string
}func NewRepo(name string) *RepoImpl {
return &RepoImpl{
name: name,
}
}func (r *RepoImpl) GetUser() string {
return r.name
}
```Assume ``Service`` with method ``Hello``:
```go
package mainimport (
"fmt"
)type Service struct {
rp Repo
}func NewService(repo Repo) *Service {
return &Service{
rp: repo,
}
}func (s *Service) Hello() {
fmt.Println("Hello User:", s.rp.GetUser())
}
```We can create ``Locator[T]`` objects:
```go
package mainimport (
"github.com/QuangTung97/svloc"
)var usernameLoc = svloc.RegisterEmpty[string]()
var repoLoc = svloc.Register[Repo](func(unv *svloc.Universe) Repo {
return NewRepo(
usernameLoc.Get(unv),
)
})var serviceLoc = svloc.Register[*Service](func(unv *svloc.Universe) *Service {
return NewService(repoLoc.Get(unv))
})
```The 3 newly created objects: ``usernameLoc``, ``repoLoc``, ``serviceLoc``
are all immutable objects and safe to use concurrently.The ``svloc.PreventRegistering`` will not allow ``Register`` functions to be called after that point.
Usually at the start of the ``main()`` function.To use in ``main()``, first creates a new ``Universe``.
Then call ``MustOverride()`` on ``usernameLoc`` to provide the username string.
And then call the ``serviceLoc.Get()`` with that ``Universe``,
All of the wiring will happen automatically:```go
package mainfunc main() {
svloc.PreventRegistering()unv := svloc.NewUniverse()
usernameLoc.MustOverride(unv, "user01")svc := serviceLoc.Get(unv)
svc.Hello()
}
```Full example:
```go
package mainimport (
"fmt"
"github.com/QuangTung97/svloc"
)type Repo interface {
GetUser() string
}type RepoImpl struct {
name string
}func NewRepo(name string) *RepoImpl {
return &RepoImpl{
name: name,
}
}func (r *RepoImpl) GetUser() string {
return r.name
}type Service struct {
rp Repo
}func NewService(repo Repo) *Service {
return &Service{
rp: repo,
}
}func (s *Service) Hello() {
fmt.Println("Hello User:", s.rp.GetUser())
}var usernameLoc = svloc.RegisterEmpty[string]()
var repoLoc = svloc.Register[Repo](func(unv *svloc.Universe) Repo {
return NewRepo(
usernameLoc.Get(unv),
)
})var serviceLoc = svloc.Register[*Service](func(unv *svloc.Universe) *Service {
return NewService(repoLoc.Get(unv))
})func main() {
svloc.PreventRegistering()unv := svloc.NewUniverse()
usernameLoc.MustOverride(unv, "user01")svc := serviceLoc.Get(unv)
svc.Hello()
}
```Using ``OnShutdown`` and ``Shutdown``:
```go
package mainvar repoLoc = svloc.Register[Repo](func(unv *svloc.Universe) Repo {
unv.OnShutdown(func() {
fmt.Println("Shutdown Repo")
})return NewRepo(
usernameLoc.Get(unv),
)
})var serviceLoc = svloc.Register[*Service](func(unv *svloc.Universe) *Service {
unv.OnShutdown(func() {
fmt.Println("Shutdown Service")
})
return NewService(repoLoc.Get(unv))
})func main() {
svloc.PreventRegistering()unv := svloc.NewUniverse()
defer unv.Shutdown()usernameLoc.MustOverride(unv, "user01")
svc := serviceLoc.Get(unv)
svc.Hello()
}
```