Status: Accepted Date: 2026-01-04 Issue: #322 ARCH-FIX-SINGLETON Author: Lead Developer (Claude Code Opus) Approver: PM (xian)
The ServiceContainer class used a singleton pattern (__new__ override with _instance class variable) to ensure a single instance across the application. While this simplified service access, it blocked horizontal scaling:
class ServiceContainer:
_instance = None
_initialized = False
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
Problems:
ServiceContainer.reset() classmethod to clean up stateAdopt application-scoped container lifecycle managed by FastAPI lifespan.
The container will be:
web/startup.py)app.state.service_containerget_container())Option 1: Per-Request Container
Rejected because:
Option 2: Keep Singleton
Rejected because:
# web/api/dependencies.py
def get_container(request: Request) -> ServiceContainer:
"""Get ServiceContainer from application state."""
if not hasattr(request.app.state, "service_container"):
raise HTTPException(status_code=503, detail="Container not initialized")
return request.app.state.service_container
class ServiceContainer:
"""Application-scoped container (NOT a singleton)."""
def __init__(self):
self._registry = ServiceRegistry()
self._initialized = False
# No __new__ override
# No _instance class variable
Routes use FastAPI dependency injection:
from fastapi import Depends
from web.api.dependencies import get_container
@router.get("/example")
async def example(container: ServiceContainer = Depends(get_container)):
service = container.get_service("my_service")
Services receive container via constructor:
class MyService:
def __init__(self, container: ServiceContainer):
self.llm = container.get_service("llm")
Depends(get_container)def test_something():
container = ServiceContainer() # Gets singleton
try:
# test code
finally:
ServiceContainer.reset() # Must reset global state
def test_something():
container = ServiceContainer() # Creates new instance
await container.initialize()
# test code
# No reset needed - container goes out of scope
get_container() DI helper (backward compatible)Accepted by PM: 2026-01-04 (implicit in Phase -1 gameplan approval)