The do library allows for the creation of a scope hierarchy where child scopes inherit services from their parents. This is useful for managing shared resources (root scope), user-specific services (user scope), and request-specific services (session scope).
Key behaviors include:
- Shared Resources: Services registered in the root scope are available to all child scopes.
- Scope Isolation: Services registered in a specific scope are isolated from sibling scopes at the same level.
- Dependency Inheritance: Child scopes can access services from parent scopes using
do.MustInvoke[T](injector). - Lifecycle Management: Scopes can be managed independently for efficient resource cleanup.
package main
import (
"fmt"
"github.com/samber/do/v2"
)
type Database interface { Connect() error; Close() error }
type UserRepository interface { FindByID(id string) (*User, error) }
type SessionManager interface { CreateSession(userID string) (string, error) }
type User struct { ID string; Name string }
// Concrete implementations
type database struct{ connectionString string }
func (d *database) Connect() error { return nil }
func (d *database) Close() error { return nil }
type userRepository struct{ db Database }
func (r *userRepository) FindByID(id string) (*User, error) { return &User{ID: id, Name: "User " + id}, nil }
type sessionManager struct{ userRepo UserRepository }
func (s *sessionManager) CreateSession(userID string) (string, error) { return "session-" + userID, nil }
func main() {
// 1. Create root scope (top-level container)
root := do.New()
// Register shared services in root
do.Provide(root, func(i do.Injector) (Database, error) {
return &database{connectionString: "postgres://localhost:5432/mydb"}, nil
})
// 2. Create child scope (e.g., user session)
userScope := do.Scope(root, "user")
// Register user-specific services
do.Provide(userScope, func(i do.Injector) (UserRepository, error) {
// Access Database from root scope
db := do.MustInvoke[Database](i)
return &userRepository{db: db}, nil
})
// 3. Create grandchild scope (e.g., individual session)
sessionScope := do.Scope(userScope, "session")
// Register session-specific services
do.Provide(sessionScope, func(i do.Injector) (SessionManager, error) {
// Access UserRepository from user scope
userRepo := do.MustInvoke[UserRepository](i)
return &sessionManager{userRepo: userRepo}, nil
})
// Accessing services: Child scopes can resolve their own and parent services
sessionMgr := do.MustInvoke[SessionManager](sessionScope)
userRepo := do.MustInvoke[UserRepository](sessionScope)
db := do.MustInvoke[Database](sessionScope)
fmt.Println("All services resolved successfully")
}