Using Scopes

Scopes are one of the most powerful features in godi, enabling proper lifecycle management for services in scenarios like web requests, background jobs, or any bounded operation. This guide shows you how to effectively use scopes.

What is a Scope?

A scope creates a boundary for service instances. Services with a Scoped lifetime are created once per scope and shared within that scope. When the scope is disposed, all scoped services are cleaned up.

// Create a scope
scope := provider.CreateScope(context.Background())
defer scope.Close() // Always close scopes!

// Services resolved from this scope share scoped instances
service1, _ := godi.Resolve[MyService](scope.ServiceProvider())
service2, _ := godi.Resolve[MyService](scope.ServiceProvider())
// service1 == service2 (same instance within scope)

Web Request Scoping

The most common use case for scopes is handling web requests:

HTTP Middleware Pattern

func DIMiddleware(provider godi.ServiceProvider) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Create a scope for this request
            scope := provider.CreateScope(r.Context())
            defer scope.Close()

            // Add scope to request context
            ctx := context.WithValue(r.Context(), "scope", scope)

            // Continue with scoped services
            next.ServeHTTP(w, r.WithContext(ctx))
        })
    }
}

// Using in a handler
func UserHandler(w http.ResponseWriter, r *http.Request) {
    scope := r.Context().Value("scope").(godi.Scope)

    userService, err := godi.Resolve[UserService](scope.ServiceProvider())
    if err != nil {
        http.Error(w, "Service error", http.StatusInternalServerError)
        return
    }