Overview
The ModuleContext class manages the relationships between modules, tracking which services are exported, imported, and globally available. It enforces encapsulation by controlling access to services based on module dependencies.
Key Features:
- Tracks module imports and exports
- Maps services to their providing modules
- Enforces access control based on module relationships
- Manages global service availability
- Provides visibility into module dependencies
Class Definition
ModuleContext is primarily used internally by the framework. You typically interact with it through the ApplicationContainer or in lifecycle hooks like onModuleInit and onModuleDestroy.
Properties
All properties are read-only and provide access to internal module relationship maps.
moduleExports
Map<Type, Set<Type>> get moduleExports
Map of module types to the set of service types they export.
Returns: Map<Type, Set<Type>> - Module type to exported service types.
Example:
// In onModuleInit
Future<void> onModuleInit(Locator locator, ModuleContext context) async {
final exports = context.moduleExports[UserModule];
print('UserModule exports: $exports');
}
moduleImports
Map<Type, Set<Type>> get moduleImports
Map of module types to the set of module types they import.
Returns: Map<Type, Set<Type>> - Module type to imported module types.
Example:
Final<void> onModuleInit(Locator locator, ModuleContext context) async {
final imports = context.moduleImports[UserModule];
print('UserModule imports: $imports');
}
serviceToModule
Map<Type, Type> get serviceToModule
Map of service types to the module type that provides them.
Returns: Map<Type, Type> - Service type to provider module type.
Example:
Future<void> onModuleInit(Locator locator, ModuleContext context) async {
final provider = context.serviceToModule[UserService];
print('UserService is provided by: $provider');
}
globalServices
Set<Type> get globalServices
Set of service types that are globally available (accessible by all modules).
Returns: Set<Type> - Globally available service types.
Example:
Future<void> onModuleInit(Locator locator, ModuleContext context) async {
if (context.globalServices.contains(UserService)) {
print('UserService is globally available');
}
}
Methods
registerModuleExports
void registerModuleExports(Type moduleType, List<Type> exports)
Register the services that a module exports.
The type of the module exporting services.
List of service types that the module exports.
Returns: void
This method is called internally by the framework during module registration. You should not call it directly.
registerModuleImports
void registerModuleImports(Type moduleType, List<Type> imports)
Register the modules that a module imports.
The type of the module importing other modules.
List of module types that are imported.
Returns: void
This method is called internally by the framework. Do not call it directly.
registerServiceProvider
void registerServiceProvider(Type serviceType, Type moduleType)
Register which module provides a specific service.
The type of service being registered.
The type of module that provides this service.
Returns: void
This method is called automatically when services are registered. Do not call it manually.
markAsGlobal
void markAsGlobal(Type serviceType)
Mark a service as globally available, making it accessible to all modules regardless of import relationships.
The service type to mark as global.
Returns: void
Services from modules directly registered with ApplicationContainer are automatically marked as global.
canAccess
bool canAccess(Type requestingModule, Type serviceType)
Check if a module can access a specific service based on import/export relationships.
The module requesting access to the service.
The service type being accessed.
Returns: bool - true if the module can access the service.
Access Rules:
- Global services are always accessible
- Services from the requesting module itself are accessible
- Services from imported modules are accessible if they are exported
Example:
class UserModule extends Module {
@override
Future<void> onModuleInit(Locator locator, ModuleContext context) async {
final canAccessDb = context.canAccess(
UserModule,
Database,
);
if (canAccessDb) {
print('UserModule can access Database');
} else {
print('UserModule cannot access Database (not exported)');
}
}
}
getAvailableServices
Set<Type> getAvailableServices(Type moduleType)
Get all services available to a specific module.
The module type to get available services for.
Returns: Set<Type> - All service types accessible by the module.
Includes:
- Global services
- Services from imported modules that are exported
- Services from the module itself
Example:
class UserModule extends Module {
@override
Future<void> onModuleInit(Locator locator, ModuleContext context) async {
final available = context.getAvailableServices(UserModule);
print('Available services: ${available.length}');
for (final serviceType in available) {
print(' - $serviceType');
}
}
}
Complete Example
import 'package:nest_core/core.dart';
// Services
class Database {}
class UserRepository {}
class UserService {}
class AuthService {}
// Database module
class DatabaseModule extends Module {
@override
Future<void> providers(Locator locator) async {
locator.registerSingleton<Database>(Database());
}
@override
List<Type> get exports => [Database];
}
// User module
class UserModule extends Module {
@override
List<Module> get imports => [DatabaseModule()];
@override
Future<void> providers(Locator locator) async {
locator.registerSingleton<UserRepository>(UserRepository());
locator.registerSingleton<UserService>(UserService());
}
@override
List<Type> get exports => [UserService];
@override
Future<void> onModuleInit(Locator locator, ModuleContext context) async {
// Inspect module context
print('\n=== Module Context Inspection ===');
// Check what UserModule exports
final exports = context.moduleExports[UserModule];
print('UserModule exports: $exports');
// Check what UserModule imports
final imports = context.moduleImports[UserModule];
print('UserModule imports: $imports');
// Check which module provides Database
final dbProvider = context.serviceToModule[Database];
print('Database is provided by: $dbProvider');
// Check if UserService is global
final isGlobal = context.globalServices.contains(UserService);
print('UserService is global: $isGlobal');
// Check access permissions
final canAccessDb = context.canAccess(UserModule, Database);
print('UserModule can access Database: $canAccessDb');
final canAccessAuth = context.canAccess(UserModule, AuthService);
print('UserModule can access AuthService: $canAccessAuth');
// Get all available services
final available = context.getAvailableServices(UserModule);
print('Available to UserModule: $available');
}
}
// App module
class AppModule extends Module {
@override
List<Module> get imports => [UserModule()];
}
void main() async {
final container = ApplicationContainer();
await container.registerModule(AppModule());
// Access module context from container
final context = container.context;
print('\n=== Container-level Context ===');
print('All registered modules: ${context.moduleImports.keys}');
print('All global services: ${context.globalServices}');
print('All service providers: ${context.serviceToModule}');
}
Output:
=== Module Context Inspection ===
UserModule exports: {UserService}
UserModule imports: {DatabaseModule}
Database is provided by: DatabaseModule
UserService is global: true
UserModule can access Database: true
UserModule can access AuthService: false
Available to UserModule: {Database, UserRepository, UserService}
=== Container-level Context ===
All registered modules: {ApplicationContainer, UserModule, DatabaseModule}
All global services: {Database, UserService}
All service providers: {Database: DatabaseModule, UserRepository: UserModule, UserService: UserModule}
Debugging Module Issues
Use ModuleContext to debug common module-related issues:
class DebugModule extends Module {
@override
Future<void> onModuleInit(Locator locator, ModuleContext context) async {
// Debug: Check if a service is exported
final serviceType = UserService;
final providerModule = context.serviceToModule[serviceType];
if (providerModule != null) {
final exports = context.moduleExports[providerModule];
if (!exports!.contains(serviceType)) {
print('WARNING: $serviceType is not exported by $providerModule');
}
}
// Debug: Check import chain
final imports = context.moduleImports[DebugModule];
print('This module imports: $imports');
for (final importedModule in imports ?? <Type>{}) {
final importExports = context.moduleExports[importedModule];
print('$importedModule exports: $importExports');
}
// Debug: List all available services
final available = context.getAvailableServices(DebugModule);
print('Services available to ${DebugModule}: $available');
}
}
Best Practices
- Use in lifecycle hooks: Access
ModuleContext in onModuleInit for debugging
- Check availability: Use
canAccess() to verify service accessibility
- Inspect during development: Use
getAvailableServices() to understand module dependencies
- Don’t modify directly: Never modify the context maps directly; use the Module API
- Debug export issues: Use
moduleExports to verify services are properly exported
- Trace provider modules: Use
serviceToModule to identify which module provides a service