Migration from Other DI Frameworks - InjectQ Documentation
Author: InjectQ Contributors
Description: Modern Python dependency injection library

Skip to content

# Migration from Other DI Frameworks

This guide covers migration from various other dependency injection frameworks to InjectQ, including framework-specific patterns and best practices.

## 🌟 Framework Comparison Overview

Framework | Container Type | Injection Style | Scopes | Async Support | Key Features
--- | --- | --- | --- | --- | ---
InjectQ | Explicit | Decorator/Manual | Flexible | Full | Performance, Testing, Profiling
dependency-injector | Explicit | Manual | Limited | Partial | Providers, Containers
punq | Explicit | Manual | Basic | No | Lightweight, Simple
lagom | Explicit | Manual | Basic | No | Service Locator
aiodine | Explicit | Decorator | Limited | Yes | Async-first

## 🔄 Migration from dependency-injector

### Container Setup

    # ❌ dependency-injector
    from dependency_injector import containers, providers

    class Container(containers.DeclarativeContainer):
        config = providers.Configuration()

        database = providers.Singleton(
            DatabaseConnection,
            config.database.url
        )

        user_service = providers.Factory(
            UserService,
            db=database
        )

    # ✅ InjectQ
    from injectq import InjectQ, Module

    class AppModule(Module):
        def configure(self):
            # Configuration
            config = Config()
            self.bind(Config, config).singleton()

            # Database
            self.bind(DatabaseConnection, lambda: DatabaseConnection(
                self.container.get(Config).database.url
            )).singleton()

            # Services
            self.bind(UserService, UserService)

    container = InjectQ()
    container.install(AppModule())

### Provider Migration

    # ❌ dependency-injector providers
    from dependency_injector import providers

    class Container(containers.DeclarativeContainer):
        # Singleton provider
        database = providers.Singleton(DatabaseConnection, host="localhost")

        # Factory provider
        user_service = providers.Factory(UserService, db=database)

        # Configuration provider
        config = providers.Configuration()

        # Callable provider
        api_client = providers.Callable(
            lambda: ApiClient(config.api.endpoint())
        )

    # ✅ InjectQ equivalent
    class AppModule(Module):
        def configure(self):
            # Singleton
            self.bind(DatabaseConnection, lambda: DatabaseConnection(host="localhost")).singleton()

            # Factory (transient by default)
            self.bind(UserService, UserService)

            # Configuration
            config = Config()
            self.bind(Config, config).singleton()

            # Callable/Factory
            self.bind(ApiClient, lambda: ApiClient(
                self.container.get(Config).api.endpoint
            ))

### Dependency Resolution

    # ❌ dependency-injector
    container = Container()
    user_service = container.user_service()  # Factory call
    database = container.database()          # Singleton call

    # ✅ InjectQ
    container = InjectQ()
    container.install(AppModule())
    user_service = container.get(UserService)  # Consistent interface
    database = container.get(DatabaseConnection)

## 🔧 Migration from punq

### Basic Setup

    # ❌ punq
    import punq

    container = punq.Container()

    # Register singleton
    container.register(DatabaseService, scope=punq.Scope.singleton)

    # Register with factory
    container.register(UserService, factory=lambda: UserService(
        container.resolve(DatabaseService)
    ))

    # Register instance
    config = Configuration()
    container.register(Configuration, instance=config)

    # ✅ InjectQ
    container = InjectQ()

    # Register singleton
    container.bind(DatabaseService, DatabaseService).singleton()

    # Register with dependencies (auto-resolved)
    container.bind(UserService, UserService)

    # Register instance
    config = Configuration()
    container.bind(Configuration, config).singleton()

### Interface Registration

    # ❌ punq
    from abc import ABC, abstractmethod

    class IUserRepository(ABC):
        @abstractmethod
        def find_user(self, user_id: str):
            pass

    class SqlUserRepository(IUserRepository):
        def find_user(self, user_id: str):
            return {"id": user_id}

    container = punq.Container()
    container.register(IUserRepository, SqlUserRepository)

    # ✅ InjectQ
    class IUserRepository(ABC):
        @abstractmethod
        def find_user(self, user_id: str):
            pass

    class SqlUserRepository(IUserRepository):
        def find_user(self, user_id: str):
            return {"id": user_id}

    container = InjectQ()
    container.bind(IUserRepository, SqlUserRepository)

## 🌊 Migration from lagom

### Service Registration

    # ❌ lagom
    from lagom import Container

    container = Container()

    # Register service
    container.register(DatabaseService)

    # Register with dependencies
    container.register(UserService, DatabaseService)

    # Register singleton
    container.register(CacheService, is_singleton=True)

    # ✅ InjectQ
    container = InjectQ()

    # Register service
    container.bind(DatabaseService, DatabaseService)

    # Register with auto-resolved dependencies
    container.bind(UserService, UserService)

    # Register singleton
    container.bind(CacheService, CacheService).singleton()

### Dependency Resolution

    # ❌ lagom
    database_service = container.resolve(DatabaseService)
    user_service = container.resolve(UserService)

    # ✅ InjectQ
    database_service = container.get(DatabaseService)
    user_service = container.get(UserService)

## ⚡ Migration from aiodine

### Async Service Registration

    # ❌ aiodine
    from aiodine import Container

    container = Container()

    # Register async service
    @container.register
    class AsyncUserService:
        async def __init__(self, db: AsyncDatabase):
            self.db = db
            await self.initialize()

        async def initialize(self):
            await self.db.connect()

    # ✅ InjectQ
    from injectq import InjectQ, inject

    container = InjectQ()

    class AsyncUserService:
        @inject
        async def __init__(self, db: AsyncDatabase):
            self.db = db
            await self.initialize()

        async def initialize(self):
            await self.db.connect()

    # Register async service
    container.bind(AsyncUserService, AsyncUserService)

### Async Resolution

    # ❌ aiodine
    async def main():
        service = await container.resolve(AsyncUserService)
        result = await service.process()

    # ✅ InjectQ
    async def main():
        service = await container.get_async(AsyncUserService)
        result = await service.process()

        # Or use async context
        async with container.async_scope():
            scoped_service = container.get(AsyncUserService)
            result = await scoped_service.process()

## 🎯 Advanced Migration Patterns

### Configuration Management

    # Generic configuration pattern
    class ConfigurationManager:
        """Manages configuration across different DI frameworks."""

        def __init__(self, environment: str = "development"):
            self.environment = environment
            self.config_data = self._load_config()

        def _load_config(self):
            """Load configuration based on environment."""
            if self.environment == "production":
                return {
                    "database": {
                        "host": "prod-db.example.com",
                        "port": 5432,
                        "name": "production_db"
                    },
                    "api": {
                        "endpoint": "https://api.example.com",
                        "timeout": 30
                    }
                }
            else:
                return {
                    "database": {
                        "host": "localhost",
                        "port": 5433,
                        "name": "test_db"
                    },
                    "api": {
                        "endpoint": "http://localhost:8080",
                        "timeout": 5
                    }
                }

        def setup_injectq_container(self) -> InjectQ:
            """Setup InjectQ container with configuration."""
            container = InjectQ()

            # Bind configuration
            container.bind(dict, self.config_data, name="config").singleton()

            # Database configuration
            db_config = DatabaseConfig(
                host=self.config_data["database"]["host"],
                port=self.config_data["database"]["port"],
                database=self.config_data["database"]["name"]
            )
            container.bind(DatabaseConfig, db_config).singleton()

            # API configuration
            api_config = ApiConfig(
                endpoint=self.config_data["api"]["endpoint"],
                timeout=self.config_data["api"]["timeout"]
            )
            container.bind(ApiConfig, api_config).singleton()

            return container

    # Usage
    config_manager = ConfigurationManager("production")
    container = config_manager.setup_injectq_container()

### Service Locator Migration

    # Migrate from Service Locator pattern to proper DI
    class ServiceLocatorMigration:
        """Help migrate from service locator pattern."""

        def __init__(self):
            self.container = InjectQ()
            self._setup_services()

        def _setup_services(self):
            """Setup services in InjectQ container."""
            # Instead of global service locator
            self.container.bind(DatabaseService, DatabaseService).singleton()
            self.container.bind(UserService, UserService)
            self.container.bind(EmailService, EmailService)

        def get_service(self, service_type):
            """Temporary method to ease migration."""
            return self.container.get(service_type)

        def migrate_to_injection(self):
            """Examples of proper dependency injection."""

            # ❌ Before: Service locator usage
            class BadUserController:
                def __init__(self, service_locator):
                    self.service_locator = service_locator

                def create_user(self, user_data):
                    user_service = self.service_locator.get_service(UserService)
                    email_service = self.service_locator.get_service(EmailService)
                    # Use services...

            # ✅ After: Proper dependency injection
            class GoodUserController:
                @inject
                def __init__(self, user_service: UserService, email_service: EmailService):
                    self.user_service = user_service
                    self.email_service = email_service

                def create_user(self, user_data):
                    # Use injected services directly
                    user = self.user_service.create_user(user_data)
                    self.email_service.send_welcome_email(user)
                    return user

    # Usage
    migration = ServiceLocatorMigration()

    # Temporary service locator interface
    user_service = migration.get_service(UserService)

    # Properly injected controller
    container = migration.container
    container.bind(GoodUserController, GoodUserController)
    controller = container.get(GoodUserController)

### Factory Pattern Migration

    # Migrate factory patterns
    class FactoryMigration:
        """Migrate various factory patterns to InjectQ."""

        def __init__(self):
            self.container = InjectQ()
            self._setup_factories()

        def _setup_factories(self):
            """Setup factory patterns in InjectQ."""

            # Simple factory
            self.container.bind(DatabaseConnection, self.create_database_connection)

            # Abstract factory
            self.container.bind(ServiceFactory, ConcreteServiceFactory).singleton()

            # Factory method
            self.container.bind(UserService, self.create_user_service)

        def create_database_connection(self) -> DatabaseConnection:
            """Factory method for database connection."""
            config = self.container.get(DatabaseConfig)
            return DatabaseConnection(
                host=config.host,
                port=config.port,
                database=config.database
            )

        def create_user_service(self) -> UserService:
            """Factory method for user service."""
            db = self.container.get(DatabaseConnection)
            cache = self.container.get(CacheService)
            return UserService(db, cache)

    # Abstract factory pattern
    from abc import ABC, abstractmethod

    class ServiceFactory(ABC):
        @abstractmethod
        def create_user_service(self) -> UserService:
            pass

        @abstractmethod
        def create_order_service(self) -> OrderService:
            pass

    class ConcreteServiceFactory(ServiceFactory):
        @inject
        def __init__(self, container: InjectQ):
            self.container = container

        def create_user_service(self) -> UserService:
            return self.container.get(UserService)

        def create_order_service(self) -> OrderService:
            return self.container.get(OrderService)

    # Usage
    migration = FactoryMigration()
    db_connection = migration.container.get(DatabaseConnection)
    factory = migration.container.get(ServiceFactory)
    user_service = factory.create_user_service()

## 🧪 Testing Migration

### Universal Testing Pattern

    # Universal testing approach for any framework migration
    class UniversalTestingMigration:
        """Universal testing patterns for DI framework migration."""

        def create_test_container(self) -> InjectQ:
            """Create container for testing."""
            container = InjectQ()

            # Mock dependencies
            container.bind(DatabaseService, MockDatabaseService).singleton()
            container.bind(EmailService, MockEmailService).singleton()
            container.bind(CacheService, MockCacheService).singleton()

            # Real services under test
            container.bind(UserService, UserService)
            container.bind(OrderService, OrderService)

            return container

        def create_integration_test_container(self) -> InjectQ:
            """Create container for integration testing."""
            container = InjectQ()

            # Test database
            test_db_config = DatabaseConfig(
                host="localhost",
                port=5433,
                database="test_db"
            )
            container.bind(DatabaseConfig, test_db_config).singleton()
            container.bind(DatabaseService, DatabaseService).singleton()

            # Mock external services
            container.bind(EmailService, MockEmailService).singleton()

            # Real services
            container.bind(UserService, UserService)

            return container

    # Mock implementations
    class MockDatabaseService:
        def __init__(self):
            self.data = {}

        def save(self, key: str, value: dict):
            self.data[key] = value

        def find(self, key: str):
            return self.data.get(key)

    class MockEmailService:
        def __init__(self):
            self.sent_emails = []

        def send_email(self, to: str, subject: str, body: str):
            self.sent_emails.append({
                "to": to,
                "subject": subject,
                "body": body
            })

    # Test example
    import unittest

    class TestUserService(unittest.TestCase):
        def setUp(self):
            self.migration = UniversalTestingMigration()
            self.container = self.migration.create_test_container()

        def test_user_creation(self):
            user_service = self.container.get(UserService)
            user_data = {"name": "John Doe", "email": "john@example.com"}

            user = user_service.create_user(user_data)

            self.assertIsNotNone(user)
            self.assertEqual(user["name"], "John Doe")

            # Check if email was sent
            email_service = self.container.get(EmailService)
            self.assertEqual(len(email_service.sent_emails), 1)
            self.assertEqual(email_service.sent_emails[0]["to"], "john@example.com")

## 📊 Performance Migration

### Performance Optimization

    # Performance optimization during migration
    class PerformanceMigration:
        """Optimize performance during DI framework migration."""

        def __init__(self):
            self.container = InjectQ()
            self._optimize_container()

        def _optimize_container(self):
            """Apply performance optimizations."""

            # Use appropriate scopes
            self.container.bind(DatabaseConnection, DatabaseConnection).singleton()
            self.container.bind(CacheService, CacheService).singleton()
            self.container.bind(ConfigService, ConfigService).singleton()

            # Transient for lightweight services
            self.container.bind(UserService, UserService)  # Default: transient
            self.container.bind(OrderService, OrderService)

            # Scoped for request-specific services
            self.container.bind(RequestContext, RequestContext).scoped()
            self.container.bind(SessionService, SessionService).scoped()

        def benchmark_migration(self, old_framework_resolver, iterations: int = 1000):
            """Benchmark performance comparison."""
            import time

            # Benchmark old framework
            start_time = time.time()
            for _ in range(iterations):
                service = old_framework_resolver()
            old_time = (time.time() - start_time) * 1000  # ms

            # Benchmark InjectQ
            start_time = time.time()
            for _ in range(iterations):
                service = self.container.get(UserService)
            new_time = (time.time() - start_time) * 1000  # ms

            improvement = ((old_time - new_time) / old_time) * 100

            print(f"Performance Comparison ({iterations} iterations):")
            print(f"Old framework: {old_time:.2f}ms")
            print(f"InjectQ: {new_time:.2f}ms")
            print(f"Improvement: {improvement:.2f}%")

            return {
                "old_time": old_time,
                "new_time": new_time,
                "improvement_percent": improvement
            }

    # Usage
    migration = PerformanceMigration()

    # Example old framework resolver
    def old_framework_resolver():
        # Simulate old framework resolution
        import time
        time.sleep(0.001)  # Simulate slower resolution
        return UserService(DatabaseService(), EmailService())

    # Benchmark
    results = migration.benchmark_migration(old_framework_resolver)

## 🎯 Migration Summary

### Universal Migration Steps

1. Analysis Phase
2. Identify current DI patterns
3. Map services and dependencies

4. Identify pain points and limitations

5. Planning Phase

6. Create migration timeline
7. Plan testing strategy

8. Identify performance requirements

9. Implementation Phase

10. Start with core services
11. Migrate module by module

12. Update tests continuously

13. Optimization Phase

14. Apply appropriate scopes
15. Optimize performance

16. Monitor memory usage

17. Validation Phase

18. Run comprehensive tests
19. Performance benchmarking
20. Production readiness check

### Common Migration Patterns

- Container Replacement: Replace old container with InjectQ
- Binding Migration: Convert registration patterns to InjectQ bindings
- Scope Optimization: Apply appropriate scopes for performance
- Testing Enhancement: Use InjectQ's testing utilities
- Async Upgrade: Add async support where beneficial

### Benefits Across All Migrations

- Consistent API: Unified interface across all services
- Better Performance: Optimized resolution and memory usage
- Enhanced Testing: Comprehensive testing utilities
- Type Safety: Better type checking and IDE support
- Async Support: Full async/await support
- Profiling Tools: Built-in performance monitoring

### Migration Best Practices

1. Gradual Migration: Migrate incrementally, not all at once
2. Test-Driven: Write tests before migrating production code
3. Performance Monitoring: Benchmark before and after migration
4. Documentation: Document migration decisions and patterns
5. Team Training: Ensure team understands new patterns
6. Rollback Plan: Have a plan to rollback if needed

This completes the migration guides section, providing comprehensive guidance for migrating from various dependency injection frameworks to InjectQ.