Advanced Examples
Complex patterns and advanced usage scenarios for NexusDI.
Circular Dependencies
Service Layer with Circular References
from nexusdi import singleton
@singleton
class UserService:
def __init__(self, order_service: 'OrderService'):
self.order_service = order_service
def get_user_orders(self, user_id: int):
return self.order_service.find_by_user(user_id)
@singleton
class OrderService:
def __init__(self, user_service: UserService):
self.user_service = user_service
def find_by_user(self, user_id: int):
# Access user_service through lazy proxy
user = self.user_service.get_user_info(user_id)
return f"Orders for {user}"
# Usage - circular dependencies are automatically resolved
initialize()
user_service = _container.resolve(UserService)
orders = user_service.get_user_orders(123)
Complex Circular Chain
@singleton
class ServiceA:
def __init__(self, service_b: 'ServiceB', service_c: 'ServiceC'):
self.service_b = service_b
self.service_c = service_c
def method_a(self):
return f"A -> {self.service_b.method_b()}"
@singleton
class ServiceB:
def __init__(self, service_c: 'ServiceC', service_a: ServiceA):
self.service_c = service_c
self.service_a = service_a
def method_b(self):
return f"B -> {self.service_c.method_c()}"
@singleton
class ServiceC:
def __init__(self, service_a: ServiceA):
self.service_a = service_a
def method_c(self):
return "C executed"
Multi-Tenant Architecture
from nexusdi import scoped, singleton, transient
import threading
@scoped
class TenantContext:
def __init__(self):
self.tenant_id = None
self.database_url = None
self.config = {}
def set_tenant(self, tenant_id: str):
self.tenant_id = tenant_id
self.database_url = f"postgresql://tenant_{tenant_id}.db"
self.config = self._load_tenant_config(tenant_id)
def _load_tenant_config(self, tenant_id: str):
return {"tenant": tenant_id, "features": ["feature1", "feature2"]}
@transient
class TenantAwareService:
def __init__(self, tenant_context: TenantContext):
self.tenant_context = tenant_context
def process_data(self, data):
tenant_id = self.tenant_context.tenant_id
return f"Processing {data} for tenant {tenant_id}"
class TenantMiddleware:
@inject
def __call__(self, request, tenant_context: TenantContext):
tenant_id = request.headers.get('X-Tenant-ID')
tenant_context.set_tenant(tenant_id)
return f"Request processed for tenant {tenant_id}"
Plugin Architecture
from abc import ABC, abstractmethod
from nexusdi import singleton, transient
class IPlugin(ABC):
@abstractmethod
def execute(self, data):
pass
@singleton
class PluginRegistry:
def __init__(self):
self.plugins = []
def register(self, plugin: IPlugin):
self.plugins.append(plugin)
def execute_all(self, data):
results = []
for plugin in self.plugins:
results.append(plugin.execute(data))
return results
@transient
class EmailPlugin(IPlugin):
def __init__(self, email_service: EmailService):
self.email_service = email_service
def execute(self, data):
return self.email_service.send_email(data['email'], data['subject'])
@transient
class LoggingPlugin(IPlugin):
def __init__(self, logger: LoggerService):
self.logger = logger
def execute(self, data):
self.logger.info(f"Processing: {data}")
return f"Logged: {data}"
@transient
class PluginManager:
def __init__(self, registry: PluginRegistry):
self.registry = registry
self._register_plugins()
def _register_plugins(self):
# Auto-register plugins
email_plugin = _container.resolve(EmailPlugin)
logging_plugin = _container.resolve(LoggingPlugin)
self.registry.register(email_plugin)
self.registry.register(logging_plugin)
Factory Pattern with DI
@singleton
class ServiceFactory:
def __init__(self, config: ConfigService):
self.config = config
def create_payment_processor(self, payment_type: str):
if payment_type == "credit_card":
return _container.resolve(CreditCardProcessor)
elif payment_type == "paypal":
return _container.resolve(PayPalProcessor)
else:
raise ValueError(f"Unknown payment type: {payment_type}")
@transient
class CreditCardProcessor:
def __init__(self, bank_service: BankService):
self.bank_service = bank_service
def process(self, amount: float):
return self.bank_service.charge_card(amount)
@transient
class PayPalProcessor:
def __init__(self, paypal_api: PayPalAPI):
self.paypal_api = paypal_api
def process(self, amount: float):
return self.paypal_api.process_payment(amount)
@transient
class PaymentService:
def __init__(self, factory: ServiceFactory):
self.factory = factory
def process_payment(self, payment_type: str, amount: float):
processor = self.factory.create_payment_processor(payment_type)
return processor.process(amount)
Command Pattern with DI
from abc import ABC, abstractmethod
from nexusdi import singleton, transient
from typing import List
class ICommand(ABC):
@abstractmethod
def execute(self):
pass
@transient
class CreateUserCommand(ICommand):
def __init__(self, user_service: UserService, username: str, email: str):
self.user_service = user_service
self.username = username
self.email = email
def execute(self):
return self.user_service.create_user(self.username, self.email)
@transient
class SendEmailCommand(ICommand):
def __init__(self, email_service: EmailService, to: str, subject: str):
self.email_service = email_service
self.to = to
self.subject = subject
def execute(self):
return self.email_service.send_email(self.to, self.subject)
@singleton
class CommandInvoker:
def __init__(self):
self.commands: List[ICommand] = []
def add_command(self, command: ICommand):
self.commands.append(command)
def execute_all(self):
results = []
for command in self.commands:
results.append(command.execute())
return results
# Usage
def create_user_workflow(username: str, email: str):
invoker = _container.resolve(CommandInvoker)
# Create commands with DI
create_cmd = CreateUserCommand(
_container.resolve(UserService),
username,
email
)
email_cmd = SendEmailCommand(
_container.resolve(EmailService),
email,
"Welcome!"
)
invoker.add_command(create_cmd)
invoker.add_command(email_cmd)
return invoker.execute_all()
Observer Pattern with Events
from typing import List, Callable
from nexusdi import singleton, transient
@singleton
class EventManager:
def __init__(self):
self.listeners = {}
def subscribe(self, event_type: str, callback: Callable):
if event_type not in self.listeners:
self.listeners[event_type] = []
self.listeners[event_type].append(callback)
def publish(self, event_type: str, data):
if event_type in self.listeners:
for callback in self.listeners[event_type]:
callback(data)
@singleton
class EmailNotificationHandler:
def __init__(self, email_service: EmailService, event_manager: EventManager):
self.email_service = email_service
event_manager.subscribe('user_created', self.handle_user_created)
event_manager.subscribe('order_placed', self.handle_order_placed)
def handle_user_created(self, user_data):
self.email_service.send_email(
user_data['email'],
"Welcome!",
f"Welcome {user_data['name']}"
)
def handle_order_placed(self, order_data):
self.email_service.send_email(
order_data['user_email'],
"Order Confirmation",
f"Order {order_data['id']} confirmed"
)
@transient
class UserService:
def __init__(self, event_manager: EventManager):
self.event_manager = event_manager
def create_user(self, name: str, email: str):
user = {"id": 123, "name": name, "email": email}
self.event_manager.publish('user_created', user)
return user
Decorator Chain Pattern
from functools import wraps
from nexusdi import singleton, transient
@singleton
class SecurityService:
def check_permissions(self, user_id: int, action: str):
return True # Simplified
@singleton
class AuditService:
def log_action(self, user_id: int, action: str):
print(f"User {user_id} performed {action}")
@transient
class DecoratedService:
def __init__(self, security: SecurityService, audit: AuditService):
self.security = security
self.audit = audit
def secure_and_audit(self, action: str):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
user_id = kwargs.get('user_id', 1)
# Security check
if not self.security.check_permissions(user_id, action):
raise PermissionError("Access denied")
# Execute function
result = func(*args, **kwargs)
# Audit log
self.audit.log_action(user_id, action)
return result
return wrapper
return decorator
# Usage
@transient
class BusinessService:
def __init__(self, decorator_service: DecoratedService):
self.decorator = decorator_service
@property
def secure_operation(self):
return self.decorator.secure_and_audit("sensitive_operation")
@secure_operation
def perform_sensitive_operation(self, data, user_id: int):
return f"Performed operation with {data}"
Async/Await Pattern
import asyncio
from nexusdi import singleton, transient
@singleton
class AsyncDatabaseService:
async def query(self, sql: str):
await asyncio.sleep(0.1) # Simulate async I/O
return f"Async result: {sql}"
@singleton
class AsyncCacheService:
def __init__(self):
self.cache = {}
async def get(self, key: str):
await asyncio.sleep(0.01)
return self.cache.get(key)
async def set(self, key: str, value):
await asyncio.sleep(0.01)
self.cache[key] = value
@transient
class AsyncUserService:
def __init__(self, db: AsyncDatabaseService, cache: AsyncCacheService):
self.db = db
self.cache = cache
async def get_user(self, user_id: int):
cache_key = f"user_{user_id}"
cached_user = await self.cache.get(cache_key)
if cached_user:
return cached_user
user_data = await self.db.query(f"SELECT * FROM users WHERE id = {user_id}")
await self.cache.set(cache_key, user_data)
return user_data
# Usage with async context
async def async_example():
initialize()
user_service = _container.resolve(AsyncUserService)
user = await user_service.get_user(123)
print(user)
# Run async example
# asyncio.run(async_example())
Custom Lifecycle Implementation
from nexusdi.core.lifecycle import LifeCycle
from nexusdi.core.container import _container
import time
class TimedLifecycle:
"""Custom lifecycle that expires instances after a specified time."""
def __init__(self, ttl_seconds: int = 300):
self.ttl_seconds = ttl_seconds
self.instances = {}
self.timestamps = {}
def get_instance(self, cls_type):
now = time.time()
# Check if instance exists and is still valid
if cls_type in self.instances:
if now - self.timestamps[cls_type] < self.ttl_seconds:
return self.instances[cls_type]
else:
# Instance expired, remove it
del self.instances[cls_type]
del self.timestamps[cls_type]
# Create new instance
instance = _container._create_instance_with_lazy_deps(cls_type)
self.instances[cls_type] = instance
self.timestamps[cls_type] = now
return instance
# Usage of custom lifecycle
timed_lifecycle = TimedLifecycle(ttl_seconds=60)
class TimedService:
def __init__(self):
self.created_at = time.time()
def get_age(self):
return time.time() - self.created_at
# Register with custom lifecycle
_container.register(TimedService, LifeCycle.SINGLETON)
# Override the resolution behavior (simplified example)
Memory-Efficient Large Object Handling
import weakref
from nexusdi import singleton, transient
@singleton
class ResourcePool:
def __init__(self):
self._pool = weakref.WeakValueDictionary()
self._stats = {"created": 0, "reused": 0}
def get_resource(self, resource_id: str):
if resource_id in self._pool:
self._stats["reused"] += 1
return self._pool[resource_id]
resource = ExpensiveResource(resource_id)
self._pool[resource_id] = resource
self._stats["created"] += 1
return resource
def get_stats(self):
return self._stats.copy()
class ExpensiveResource:
def __init__(self, resource_id: str):
self.resource_id = resource_id
self.large_data = [i for i in range(100000)] # Simulate large object
def process(self, data):
return f"Processing {data} with resource {self.resource_id}"
@transient
class ResourceManager:
def __init__(self, pool: ResourcePool):
self.pool = pool
def process_with_resource(self, resource_id: str, data):
resource = self.pool.get_resource(resource_id)
return resource.process(data)
Testing with Complex Dependencies
import pytest
from unittest.mock import Mock, patch
from nexusdi import singleton, transient, _container, initialize
# Production services
@singleton
class ExternalApiService:
def call_api(self, endpoint: str):
return f"Real API call to {endpoint}"
@singleton
class DatabaseService:
def query(self, sql: str):
return f"Real DB query: {sql}"
@transient
class BusinessLogic:
def __init__(self, api: ExternalApiService, db: DatabaseService):
self.api = api
self.db = db
def complex_operation(self, data):
api_result = self.api.call_api("/process")
db_result = self.db.query("SELECT * FROM processed")
return f"Business logic: {api_result} + {db_result} + {data}"
# Test setup with mocks
@pytest.fixture
def mock_container():
# Create mock services
mock_api = Mock(spec=ExternalApiService)
mock_api.call_api.return_value = "Mock API response"
mock_db = Mock(spec=DatabaseService)
mock_db.query.return_value = "Mock DB response"
# Override container dependencies
original_api_dep = _container._dependencies.get(ExternalApiService)
original_db_dep = _container._dependencies.get(DatabaseService)
_container._dependencies[ExternalApiService] = Mock()
_container._dependencies[ExternalApiService].instance = mock_api
_container._dependencies[DatabaseService] = Mock()
_container._dependencies[DatabaseService].instance = mock_db
yield
# Restore original dependencies
if original_api_dep:
_container._dependencies[ExternalApiService] = original_api_dep
if original_db_dep:
_container._dependencies[DatabaseService] = original_db_dep
def test_complex_business_logic(mock_container):
business_logic = _container.resolve(BusinessLogic)
result = business_logic.complex_operation("test_data")
assert "Mock API response" in result
assert "Mock DB response" in result
assert "test_data" in result
# Performance testing
def test_performance_with_di():
initialize()
import time
start_time = time.time()
# Resolve multiple times to test caching
for _ in range(1000):
service = _container.resolve(BusinessLogic)
end_time = time.time()
duration = end_time - start_time
print(f"1000 resolutions took {duration:.4f} seconds")
assert duration < 1.0 # Should be fast due to singleton caching
These advanced examples demonstrate sophisticated patterns like circular dependencies, multi-tenancy, plugin architectures, and complex testing scenarios that showcase NexusDI's flexibility and power.