Service Providers

A powerful dependency injection and service registration system for organizing and bootstrapping your Khadem application.

Dependency InjectionService RegistrationBoot LifecycleModular Architecture

System Overview

Service Provider Architecture

Service providers are the foundation of Khadem's dependency injection system. They provide a clean, organized way to register services, configure dependencies, and execute initialization logic.

Core Components

  • ServiceProviderManager - Main orchestrator
  • ServiceProviderRegistry - Provider registration
  • ServiceProviderBootloader - Boot lifecycle
  • ServiceProviderValidator - Validation system

Key Features

  • Deferred loading support
  • Async boot lifecycle
  • Type-safe provider management
  • Validation and error handling

Provider Lifecycle

1

Registration

Provider is registered with the container. Services and dependencies are bound.

2

Boot

After all providers are registered, boot() method is called for initialization.

3

Ready

Application is fully initialized and all services are available.

Quick Start

Basic Setup

1. Create a Service Provider

dart
import 'package:khadem/khadem.dart';

class DatabaseServiceProvider extends ServiceProvider {
  @override
  void register(ContainerInterface container) {
    // Register database connection
    container.singleton(DatabaseConnection.new);

    // Register repository classes
    container.singleton<UserRepository>(UserRepository.new);
    container.singleton<PostRepository>(PostRepository.new);
  }

  @override
  Future<void> boot(ContainerInterface container) async {
    // Initialize database connection
    final db = container.make<DatabaseConnection>();
    await db.connect();

    print('โœ… Database service initialized');
  }
}

2. Register the Provider

dart
import 'package:khadem/khadem.dart';

void main() async {
  // Initialize Khadem
  await Khadem.initialize();

  // Create service provider manager
  final container = Khadem.container;
  final providerManager = ServiceProviderManager(container);

  // Register providers
  providerManager.register(DatabaseServiceProvider());
  providerManager.register(CacheServiceProvider());
  providerManager.register(AuthServiceProvider());

  // Boot all providers
  await providerManager.bootAll();

  print('๐Ÿš€ Application started with all services');
}

Provider Types

Regular Providers

dart
import 'package:khadem/khadem.dart';

class CacheServiceProvider extends ServiceProvider {
  @override
  void register(ContainerInterface container) {
    // Register cache implementation
    container.singleton<CacheInterface>(RedisCache.new);

    // Register cache manager
    container.singleton(CacheManager.new);
  }

  @override
  Future<void> boot(ContainerInterface container) async {
    final cache = container.make<CacheInterface>();
    await cache.connect();

    print('โœ… Cache service initialized');
  }

  // This is a regular provider (loaded immediately)
  @override
  bool get isDeferred => false;
}

Loaded immediately when the application starts. Use for core services and essential dependencies.

Deferred Providers

dart
import 'package:khadem/khadem.dart';

class EmailServiceProvider extends ServiceProvider {
  @override
  void register(ContainerInterface container) {
    // Register email service
    container.singleton<EmailService>(SmtpEmailService.new);

    // Register mailer
    container.singleton(Mailer.new);
  }

  @override
  Future<void> boot(ContainerInterface container) async {
    final emailService = container.make<EmailService>();
    await emailService.verifyConnection();

    print('โœ… Email service initialized');
  }

  // This is a deferred provider (loaded only when needed)
  @override
  bool get isDeferred => true;
}

Loaded only when their services are actually needed. Improves application startup performance.

Advanced Usage

Complex Service Registration

dart
import 'package:khadem/khadem.dart';

class ApiServiceProvider extends ServiceProvider {
  @override
  void register(ContainerInterface container) {
    // Register HTTP client with configuration
    container.singleton<HttpClient>(() {
      final config = container.make<Config>();
      return HttpClient(
        baseUrl: config.get('api.base_url'),
        timeout: Duration(seconds: config.get('api.timeout', 30)),
        headers: {
          'Authorization': 'Bearer ${config.get('api.token')}',
          'Accept': 'application/json',
        },
      );
    });

    // Register API services
    container.singleton<UserApiService>(UserApiService.new);
    container.singleton<PostApiService>(PostApiService.new);
    container.singleton<NotificationApiService>(NotificationApiService.new);

    // Register API client facade
    container.singleton<ApiClient>(ApiClient.new);
  }

  @override
  Future<void> boot(ContainerInterface container) async {
    final httpClient = container.make<HttpClient>();
    final apiClient = container.make<ApiClient>();

    // Test API connectivity
    try {
      await httpClient.get('/health');
      print('โœ… API connection established');
    } catch (e) {
      print('โŒ API connection failed: $e');
      rethrow;
    }

    // Initialize API services
    await apiClient.initialize();
  }
}

Boot-time Initialization

dart
import 'package:khadem/khadem.dart';

class MonitoringServiceProvider extends ServiceProvider {
  @override
  void register(ContainerInterface container) {
    // Register monitoring services
    container.singleton<MetricsCollector>(MetricsCollector.new);
    container.singleton<HealthChecker>(HealthChecker.new);
    container.singleton<AlertManager>(AlertManager.new);
  }

  @override
  Future<void> boot(ContainerInterface container) async {
    final metrics = container.make<MetricsCollector>();
    final healthChecker = container.make<HealthChecker>();
    final alertManager = container.make<AlertManager>();

    // Start background monitoring tasks
    metrics.startCollecting();
    healthChecker.startPeriodicChecks();

    // Set up alert handlers
    alertManager.registerHandler('database', (alert) {
      print('๐Ÿšจ Database alert: ${alert.message}');
      // Send notification, log to external service, etc.
    });

    alertManager.registerHandler('memory', (alert) {
      print('๐Ÿšจ Memory alert: ${alert.message}');
      // Trigger garbage collection, scale resources, etc.
    });

    // Start alert manager
    await alertManager.start();

    print('โœ… Monitoring system initialized');
  }
}

Provider Management

dart
import 'package:khadem/khadem.dart';

class ProviderManager {
  final ServiceProviderManager _manager;

  ProviderManager(ContainerInterface container)
      : _manager = ServiceProviderManager(container);

  // Register core providers
  void registerCoreProviders() {
    _manager.registerAll([
      DatabaseServiceProvider(),
      CacheServiceProvider(),
      AuthServiceProvider(),
      RoutingServiceProvider(),
    ]);
  }

  // Register optional providers
  void registerOptionalProviders() {
    _manager.registerAll([
      EmailServiceProvider(),
      QueueServiceProvider(),
      MonitoringServiceProvider(),
    ]);
  }

  // Boot providers in phases
  Future<void> bootApplication() async {
    // Phase 1: Boot core services first
    await _manager.bootNonDeferred();

    // Phase 2: Boot deferred services when needed
    // This can be called later when services are actually needed
    // await _manager.bootDeferred();

    print('โœ… Application booted successfully');
  }

  // Get provider statistics
  void printProviderStats() {
    print('\n๐Ÿ“Š Provider Statistics:');
    print('Total providers: ${_manager.providerCount}');
    print('Booted: ${_manager.isBooted ? 'Yes' : 'No'}');
    print('Deferred providers: ${_manager.deferredProviders.length}');
    print('Non-deferred providers: ${_manager.nonDeferredProviders.length}');

    // Validate providers
    final validationErrors = _manager.validateProviders();
    if (validationErrors.isEmpty) {
      print('โœ… All providers are valid');
    } else {
      print('โŒ Validation errors:');
      for (final error in validationErrors) {
        print('  - $error');
      }
    }
  }

  // Get providers by type
  List<DatabaseServiceProvider> getDatabaseProviders() {
    return _manager.getProvidersByType<DatabaseServiceProvider>();
  }

  // Clean up
  void reset() {
    _manager.clear();
    print('๐Ÿงน All providers cleared');
  }
}

// Usage
final providerManager = ProviderManager(container);
providerManager.registerCoreProviders();
await providerManager.bootApplication();
providerManager.printProviderStats();

Management Operations

Registration
  • register(provider) - Register single provider
  • registerAll(providers) - Register multiple providers
  • isRegistered(provider) - Check if registered
Boot Control
  • bootAll() - Boot all providers
  • bootNonDeferred() - Boot non-deferred only
  • bootDeferred() - Boot deferred only

Validation & Error Handling

dart
import 'package:khadem/khadem.dart';

class ProviderValidator {
  final ServiceProviderManager _manager;

  ProviderValidator(this._manager);

  // Validate all providers before booting
  bool validateAllProviders() {
    final errors = _manager.validateProviders();

    if (errors.isEmpty) {
      print('โœ… All providers passed validation');
      return true;
    } else {
      print('โŒ Provider validation failed:');
      for (final error in errors) {
        print('  - $error');
      }
      return false;
    }
  }

  // Custom validation logic
  List<String> customValidation() {
    final errors = <String>[];
    final providers = _manager.allProviders;

    // Check for required providers
    final hasDatabase = providers.any((p) => p is DatabaseServiceProvider);
    if (!hasDatabase) {
      errors.add('DatabaseServiceProvider is required but not registered');
    }

    // Check for circular dependencies (simplified example)
    final dependencyGraph = _buildDependencyGraph(providers);
    final cycles = _detectCycles(dependencyGraph);
    if (cycles.isNotEmpty) {
      errors.add('Circular dependencies detected: $cycles');
    }

    // Check provider naming conventions
    for (final provider in providers) {
      if (!provider.runtimeType.toString().endsWith('ServiceProvider')) {
        errors.add('${provider.runtimeType} does not follow naming convention');
      }
    }

    return errors;
  }

  Map<String, List<String>> _buildDependencyGraph(List<ServiceProvider> providers) {
    // Simplified dependency analysis
    return {};
  }

  List<String> _detectCycles(Map<String, List<String>> graph) {
    // Simplified cycle detection
    return [];
  }

  // Validate provider boot order
  List<String> validateBootOrder() {
    final errors = <String>[];
    final deferred = _manager.deferredProviders;
    final nonDeferred = _manager.nonDeferredProviders;

    // Ensure critical services are not deferred
    final criticalServices = ['DatabaseServiceProvider', 'AuthServiceProvider'];
    for (final service in criticalServices) {
      final isDeferred = deferred.any((p) => p.runtimeType.toString() == service);
      if (isDeferred) {
        errors.add('$service should not be deferred (critical service)');
      }
    }

    return errors;
  }
}

// Usage
final validator = ProviderValidator(manager);

if (validator.validateAllProviders()) {
  final customErrors = validator.customValidation();
  final bootErrors = validator.validateBootOrder();

  if (customErrors.isEmpty && bootErrors.isEmpty) {
    await manager.bootAll();
  } else {
    print('โŒ Custom validation failed:');
    [...customErrors, ...bootErrors].forEach(print);
  }
}

Validation Features

  • Automatic provider validation before registration
  • Detection of duplicate providers
  • Dependency validation (extensible)
  • Detailed error reporting with specific issues
  • Graceful handling of validation failures

Best Practices

โœ… Do's

  • Use deferred providers for non-essential services to improve startup performance
  • Keep provider logic focused and single-responsibility
  • Use meaningful names for your providers and services
  • Implement proper error handling in boot() methods
  • Document dependencies and requirements in provider comments
  • Group related services in the same provider when logical
  • Use the container for dependency injection instead of manual instantiation
  • Test your providers thoroughly, especially boot logic

โŒ Don'ts

  • Don't perform heavy operations in register() - use boot() instead
  • Don't create circular dependencies between providers
  • Don't skip validation - always validate your providers
  • Don't use providers for one-off utilities - use regular classes
  • Don't make all providers deferred - core services need immediate loading
  • Don't ignore boot errors - handle them appropriately
  • Don't register the same provider multiple times
  • Don't put business logic in providers - keep them focused on DI

API Reference

ServiceProvider Interface

dart
abstract class ServiceProvider {
  /// Called when the provider is registered in the container.
  /// Use this method to bind services and dependencies.
  void register(ContainerInterface container);

  /// Called after all providers are registered.
  /// Use this method for initialization logic that requires other services.
  Future<void> boot(ContainerInterface container) async {}

  /// If true, the provider is deferred and only loaded when needed.
  /// Deferred providers improve startup performance.
  bool get isDeferred => false;
}

// Example implementation
class MyServiceProvider extends ServiceProvider {
  @override
  void register(ContainerInterface container) {
    // Register services here
    container.singleton(MyService.new);
  }

  @override
  Future<void> boot(ContainerInterface container) async {
    // Initialization logic here
    final service = container.make<MyService>();
    await service.initialize();
  }

  @override
  bool get isDeferred => true; // Optional: make it deferred
}

ServiceProviderManager

dart
class ServiceProviderManager {
  // Constructor
  ServiceProviderManager(ContainerInterface container)

  // Registration methods
  void register(ServiceProvider provider)           // Register single provider
  void registerAll(List<ServiceProvider> providers) // Register multiple providers

  // Boot methods
  Future<void> bootAll()                    // Boot all providers
  Future<void> bootNonDeferred()            // Boot only non-deferred providers
  Future<void> bootDeferred()               // Boot only deferred providers

  // Query methods
  List<ServiceProvider> get allProviders           // Get all providers
  bool get isBooted                               // Check if all providers are booted
  List<String> validateProviders()                 // Validate all providers
  bool get areAllValid                            // Check if all providers are valid
  List<T> getProvidersByType<T>()                 // Get providers by type
  List<ServiceProvider> get deferredProviders     // Get deferred providers
  List<ServiceProvider> get nonDeferredProviders  // Get non-deferred providers
  int get providerCount                           // Get total provider count

  // Utility methods
  void clear()                                    // Clear all providers and reset state
}

ServiceProviderRegistry

dart
class ServiceProviderRegistry {
  // Constructor
  ServiceProviderRegistry(ContainerInterface container)

  // Properties
  List<ServiceProvider> get providers  // Get all registered providers (unmodifiable)

  // Registration methods
  void register(ServiceProvider provider)           // Register single provider
  void registerAll(List<ServiceProvider> providers) // Register multiple providers

  // Query methods
  bool isRegistered(ServiceProvider provider)      // Check if provider is registered
  List<T> getProvidersByType<T>()                  // Get providers by type
  List<ServiceProvider> getDeferredProviders()     // Get deferred providers
  List<ServiceProvider> getNonDeferredProviders()  // Get non-deferred providers
  int get count                                    // Get total provider count

  // Utility methods
  void clear()                                     // Clear all registered providers
}

ServiceProviderBootloader

dart
class ServiceProviderBootloader {
  // Constructor
  ServiceProviderBootloader(ContainerInterface container)

  // Properties
  bool get isBooted  // Check if all providers have been booted

  // Boot methods
  Future<void> bootProvider(ServiceProvider provider)         // Boot single provider
  Future<void> bootProviders(List<ServiceProvider> providers)  // Boot multiple providers
  Future<void> bootAll(List<ServiceProvider> providers)        // Boot all providers
  Future<void> bootNonDeferred(List<ServiceProvider> providers) // Boot non-deferred only
  Future<void> bootDeferred(List<ServiceProvider> providers)    // Boot deferred only

  // Utility methods
  void reset()  // Reset boot state
}

On this page