How to register Singleton vs Scoped services
Understand when to use global singletons, request-scoped instances, and transient dependencies.
ZCore's IoCContainer manages three distinct dependency lifecycles to balance performance and thread/request isolation.
Supported Lifecycles
1. Singleton (Application-Wide)
Instantiated once and shared across the entire lifespan of the application.
from zcore.kernel.di import container
# Pass the interface and the pre-constructed instance
container.register_singleton(RedisClient, RedisClient())- Best for: External client pools (Redis, S3/MinIO), shared configuration objects, or resource-heavy factories.
2. Scoped (Request-Wide)
Resolved once per HTTP request context (scope_id) and cached for the duration of that request. It is automatically purged when the connection closes.
# Register an implementation to be resolved per request scope
container.register_scoped(IUnitOfWork, UnitOfWork)
# Or register a pre-constructed instance into the active scope (used by middlewares)
container.register_scoped_instance(AsyncSession, db_session)- Best for: Database sessions, user context objects, transaction units.
3. Transient (Always Instantiated)
A completely new instance is created on every resolution request.
container.register_transient(PdfReportGenerator, PdfReportGenerator)- Best for: Stateless helper classes, dynamic template compilers, lightweight utilities.
Automatic Auto-Wiring:
You do not need to manually register your BaseRepository or BaseService subclasses. ZCore's IoC container automatically auto-wires them on the fly and binds them to the active request-scoped AsyncSession managed by ScopedDependencyMiddleware.