Repository URL to install this package:
|
Version:
3.0.6 ▾
|
sincpro-framework
/
ioc.py
|
|---|
"""Inversion of Control (IoC) container for the SincPro Framework"""
from enum import Enum
from functools import wraps
from typing import Any, Callable, TypeVar, Union
from dependency_injector import containers, providers
from dependency_injector.providers import Dict, Factory, Object, Singleton
from sincpro_log.logger import LoggerProxy
# Type variable for decorator return type
T = TypeVar("T", bound=type)
from .bus import ApplicationServiceBus, FeatureBus, FrameworkBus
from .exceptions import DTOAlreadyRegistered
from .sincpro_abstractions import TypeDTO
# ---------------------------------------------------------------------------------------------
# Container Definition
# ---------------------------------------------------------------------------------------------
class FrameworkContainer(containers.DeclarativeContainer):
"""Main container for the framework
This container will initialize the
main components of the framework at runtime.
"""
logger_bus: Object[LoggerProxy] = providers.Object()
injected_dependencies: Dict = providers.Dict()
dto_registry: Dict = Dict({})
# atomic layer
feature_registry: Dict = providers.Dict({})
feature_bus: Singleton[FeatureBus] = providers.Singleton(FeatureBus, logger_bus)
# orchestration layer
app_service_registry: Dict = providers.Dict({})
app_service_bus: Singleton[ApplicationServiceBus] = providers.Singleton(
ApplicationServiceBus, logger_bus
)
# Facade
framework_bus: Factory[FrameworkBus] = providers.Factory(
FrameworkBus,
feature_bus=feature_bus,
app_service_bus=app_service_bus,
logger_bus=logger_bus,
)
# ---------------------------------------------------------------------------------------------
# Service Types
# ---------------------------------------------------------------------------------------------
class ServiceType(Enum):
FEATURE = "feature"
APP_SERVICE = "app_service"
# ---------------------------------------------------------------------------------------------
# Build processes
# ---------------------------------------------------------------------------------------------
def _register_service(
framework_container: FrameworkContainer,
service_type: ServiceType,
dto: Union[str, list, TypeDTO, Any],
decorated_class: type,
) -> None:
"""Register a service (feature or app_service) to the framework container"""
# Normalize DTOs to list
dto_list = dto if isinstance(dto, list) else [dto]
for data_transfer_object in dto_list:
dto_name = data_transfer_object.__name__
# Check if DTO is already registered
if (
service_type == ServiceType.FEATURE
and dto_name in framework_container.feature_bus.kwargs
):
raise DTOAlreadyRegistered(
f"The DTO: [{dto_name} from {data_transfer_object.__module__}] is already registered as a feature"
)
if (
service_type == ServiceType.APP_SERVICE
and dto_name in framework_container.app_service_bus.kwargs
):
raise DTOAlreadyRegistered(
f"The DTO: [{dto_name} from {data_transfer_object.__module__}] is already registered as an application service"
)
# Log registration
framework_container.logger_bus.debug(f"Registering {service_type}: [{dto_name}]")
# Update DTO registry with new DTO
framework_container.dto_registry = providers.Dict(
{**{dto_name: data_transfer_object}, **framework_container.dto_registry.kwargs}
)
# Update container registry and bus attributes
match service_type:
case ServiceType.FEATURE:
framework_container.feature_registry = providers.Dict(
{
**{dto_name: providers.Factory(decorated_class)},
**framework_container.feature_registry.kwargs,
}
)
framework_container.feature_bus.add_attributes(
feature_registry=framework_container.feature_registry
)
case ServiceType.APP_SERVICE:
framework_container.app_service_registry = providers.Dict(
{
**{
dto_name: providers.Factory(
decorated_class, framework_container.feature_bus
)
},
**framework_container.app_service_registry.kwargs,
}
)
framework_container.app_service_bus.add_attributes(
app_service_registry=framework_container.app_service_registry
)
def inject_feature_to_bus(
framework_container: FrameworkContainer, dto: Union[str, list, TypeDTO, Any]
) -> Callable[[T], T]:
"""Decorator to register a feature to the framework bus
Args:
framework_container: The IoC container instance
dto: DTO or list of DTOs to register with the feature
Returns:
Decorated class with feature registration functionality
"""
@wraps(inject_feature_to_bus)
def decorator(decorated_class: T) -> T:
_register_service(framework_container, ServiceType.FEATURE, dto, decorated_class)
return decorated_class
return decorator
def inject_app_service_to_bus(
framework_container: FrameworkContainer, dto: Union[str, list]
) -> Callable[[T], T]:
"""Decorator to register an application service to the framework bus
Args:
framework_container: The IoC container instance
dto: DTO or list of DTOs to register with the application service
Returns:
Decorated class with app service registration functionality
"""
@wraps(inject_app_service_to_bus)
def decorator(decorated_class: T) -> T:
_register_service(framework_container, ServiceType.APP_SERVICE, dto, decorated_class)
return decorated_class
return decorator