Scopes
This example demonstrates how to use Dependy
to manage scoped services alongside singleton services in a Dart application.
Overview
-
Scoped Services: These services are created and disposed of within a specific scope. They are useful when you need a temporary service that should not persist beyond the context of a particular operation or request.
-
Singleton Services: These services are created once and live for the duration of the application. They are shared across the app and remain active until the application ends.
In this example, we have:
- A LoggerService that logs messages.
- A DatabaseService with two implementations:
SqlDatabaseService
andSqliteDatabaseService
. - An ApiService that fetches data, with two implementations:
ApiServiceImpl
(real API service) andMockApiService
(mocked service for testing).
The example demonstrates how to configure these services with Dependy
and use them within both a singleton and scoped module.
Services
LoggerService
The LoggerService
is an abstract class used for logging messages.
Implementation
class ConsoleLoggerService extends LoggerService {
void log(String message) {
print('[Logger]: $message');
}
}
DatabaseService
The DatabaseService
is an abstract class for managing database connections. There are two implementations:
SqlDatabaseService
: Simulates a connection to an SQL database.SqliteDatabaseService
: Simulates a connection to a SQLite database.
Implementation
class SqlDatabaseService extends DatabaseService {
SqlDatabaseService(this._logger);
final LoggerService _logger;
void connect() {
_logger.log('Connected to Sql Database');
}
void close() {
_logger.log('Closed Sql Database');
}
}
class SqliteDatabaseService extends DatabaseService {
SqliteDatabaseService(this._logger);
final LoggerService _logger;
void connect() {
_logger.log('Connected to Sqlite Database');
}
void close() {
_logger.log('Closed Sqlite Database');
}
}
ApiService
The ApiService
abstract class defines methods for fetching data and disposing of resources.
There are two implementations:
ApiServiceImpl
: A real API service.MockApiService
: A mock API service for testing.
Implementation
class ApiServiceImpl extends ApiService {
ApiServiceImpl(this._db, this._logger);
final DatabaseService _db;
final LoggerService _logger;
void fetchData() {
_logger.log('Fetching data from API...');
_db.connect();
}
void dispose() {
_logger.log('Disposing $this');
}
}
class MockApiService extends ApiService {
MockApiService(this._db, this._logger);
final DatabaseService _db;
final LoggerService _logger;
void fetchData() {
_logger.log('Mocking API data...');
_db.connect();
}
void dispose() {
_logger.log('Disposing $this');
}
}
Modules
Singleton Module
The singletonModule
defines services that are created once and shared throughout the application. These services are available globally.
final singletonModule = DependyModule(
key: 'singleton_module',
providers: {
DependyProvider<LoggerService>(
(resolve) => ConsoleLoggerService(),
key: 'singleton_logger_service',
),
DependyProvider<DatabaseService>(
(dependy) async {
final logger = await dependy<LoggerService>();
return SqlDatabaseService(logger);
},
key: 'singleton_database_service',
dependsOn: {LoggerService},
),
DependyProvider<ApiService>(
(dependy) async {
final databaseService = await dependy<DatabaseService>();
final logger = await dependy<LoggerService>();
return ApiServiceImpl(databaseService, logger);
},
key: 'singleton_api_service',
dependsOn: {DatabaseService, LoggerService},
),
},
);
Scoped Module
The scopedModule
defines services that are scoped to a specific context or operation. These services are disposed of once the scope ends.
final scopedModule = DependyModule(
key: 'scoped_module',
providers: {
// override the DatabaseService from singletonModule
DependyProvider<DatabaseService>(
(dependy) async {
final logger = await dependy<LoggerService>();
return SqliteDatabaseService(logger);
},
key: 'scoped_database_service',
dependsOn: {LoggerService},
// called when the scopedModule is disposed
dispose: (database) {
database?.close();
},
),
// override the ApiService from singletonModule
DependyProvider<ApiService>(
(dependy) async {
final database = await dependy<DatabaseService>();
final logger = await dependy<LoggerService>();
return MockApiService(database, logger);
},
dependsOn: {DatabaseService, LoggerService},
// called when the scopedModule is disposed
dispose: (api) {
api?.dispose();
},
),
},
modules: {singletonModule},
);
Usage Example
Scoped Module Usage
The following code demonstrates how to use the scopedModule
to fetch data from a mock API. After the task is done, the scoped services are disposed of.
void main() async {
print('=== Scoped Module Usage ===');
final scopedApiService = await scopedModule<ApiService>();
scopedApiService.fetchData();
scopedModule.dispose(); // Disposes all services in the scoped module
// Output:
// [Logger]: Mocking API data...
// [Logger]: Connected to Sqlite Database
// [Logger]: Closed Sqlite Database
// [Logger]: Disposing Instance of 'MockApiService'
}
Singleton Module Usage
After the scopedModule
is disposed of, we can use the singleton services to fetch data from a real API.
// Singleton Module Usage
print('\n=== Singleton Module Usage After Scoped Module Disposed ===');
final singletonApiService = await singletonModule<ApiService>();
singletonApiService.fetchData();
// Output:
// [Logger]: Fetching data from API...
// [Logger]: Connected to Sql Database
}
Key Concepts
- Scoped Services: Temporary services that are disposed of when they are no longer needed. Ideal for situations like request-based services.
- Singleton Services: Persistent services that are created once and shared across the entire application.