Injection scopes
dygo.Provide(NewRequestMeta, dygo.WithScope(dygo.RequestScoped)) // may inject *http.Request
type Controller struct{ meta dygo.Scoped[*RequestMeta] }meta, err := c.meta.Get(ctx) // one instance per requestSingletons (including every controller) reach request scoped values only through dygo.Scoped[T]. Implement OnRequestEnd(ctx) for per-request cleanup.
Durable providers (multi-tenancy)
Section titled “Durable providers (multi-tenancy)”A durable provider is request scoped, but built once per context ID (usually a tenant) and shared by every request with that ID:
dygo.Provide(NewTenantDB, dygo.WithScope(dygo.RequestScoped), dygo.Durable())
func NewTenantDB(cid dygo.ContextID, pools *Pools) (*TenantDB, error) { return pools.Open(cid.ID) }
app, err := dygo.Create(ctx, AppModule(), dygo.WithContextIDStrategy(func(r *http.Request) (string, any) { t := r.Header.Get("X-Tenant-ID") if !knownTenant(t) { return "", nil // no ID: durable providers act per request } return t, t // id, payload (cid.Payload)}))Controllers still use dygo.Scoped[*TenantDB]. A durable provider may inject dygo.ContextID, singletons and other durable providers; injecting *http.Request or a request scoped provider that is not durable fails at boot, since the instance outlives the request. Each instance is built exactly once per ID, even when requests race. Outside HTTP (a queue job, a microservice handler), dygo.WithDurableContext(ctx, tenant, payload) makes Scoped.Get reach the same instances. app.ReleaseContext(ctx, tenant) drops a tenant’s instances, and they get OnApplicationShutdown then and at app shutdown. Validate the ID in the strategy: every new ID builds and keeps new instances.