|
1 | 1 | // ignore_for_file: public_member_api_docs |
2 | 2 |
|
| 3 | +import 'dart:async'; |
3 | 4 | import 'package:core/core.dart'; |
4 | 5 | import 'package:data_mongodb/data_mongodb.dart'; |
5 | 6 | import 'package:data_repository/data_repository.dart'; |
@@ -40,11 +41,12 @@ class AppDependencies { |
40 | 41 | /// Provides access to the singleton instance. |
41 | 42 | static AppDependencies get instance => _instance; |
42 | 43 |
|
43 | | - bool _isInitialized = false; |
44 | | - Object? _initializationError; |
45 | | - StackTrace? _initializationStackTrace; |
46 | 44 | final _log = Logger('AppDependencies'); |
47 | 45 |
|
| 46 | + // A Completer to manage the one-time asynchronous initialization. |
| 47 | + // This ensures the initialization logic runs only once. |
| 48 | + Completer<void>? _initCompleter; |
| 49 | + |
48 | 50 | // --- Late-initialized fields for all dependencies --- |
49 | 51 |
|
50 | 52 | // Database |
@@ -80,15 +82,27 @@ class AppDependencies { |
80 | 82 | /// |
81 | 83 | /// This method is idempotent; it will only run the initialization logic once. |
82 | 84 | Future<void> init() async { |
83 | | - // If initialization previously failed, re-throw the original error. |
84 | | - if (_initializationError != null) { |
85 | | - return Future.error(_initializationError!, _initializationStackTrace); |
| 85 | + // If _initCompleter is not null, it means initialization is either in |
| 86 | + // progress or has already completed. Return its future to allow callers |
| 87 | + // to await the single, shared initialization process. |
| 88 | + if (_initCompleter != null) { |
| 89 | + return _initCompleter!.future; |
86 | 90 | } |
87 | 91 |
|
88 | | - if (_isInitialized) return; |
| 92 | + // This is the first call to init(). Create the completer and start the |
| 93 | + // initialization process. |
| 94 | + _initCompleter = Completer<void>(); |
| 95 | + _log.info('Starting application dependency initialization...'); |
| 96 | + _initializeDependencies(); |
89 | 97 |
|
90 | | - _log.info('Initializing application dependencies...'); |
| 98 | + // Return the future from the completer. |
| 99 | + return _initCompleter!.future; |
| 100 | + } |
91 | 101 |
|
| 102 | + /// The core logic for initializing all dependencies. |
| 103 | + /// This method is private and should only be called once by [init]. |
| 104 | + Future<void> _initializeDependencies() async { |
| 105 | + _log.info('Initializing application dependencies...'); |
92 | 106 | try { |
93 | 107 | // 1. Initialize Database Connection |
94 | 108 | _mongoDbConnectionManager = MongoDbConnectionManager(); |
@@ -271,24 +285,33 @@ class AppDependencies { |
271 | 285 | cacheDuration: EnvironmentConfig.countryServiceCacheDuration, |
272 | 286 | ); |
273 | 287 |
|
274 | | - _isInitialized = true; |
275 | 288 | _log.info('Application dependencies initialized successfully.'); |
| 289 | + // Signal that initialization has completed successfully. |
| 290 | + _initCompleter!.complete(); |
276 | 291 | } catch (e, s) { |
277 | 292 | _log.severe('Failed to initialize application dependencies', e, s); |
278 | | - _initializationError = e; |
279 | | - _initializationStackTrace = s; |
| 293 | + // Signal that initialization has failed. |
| 294 | + _initCompleter!.completeError(e, s); |
280 | 295 | rethrow; |
281 | 296 | } |
282 | 297 | } |
283 | 298 |
|
284 | 299 | /// Disposes of resources, such as closing the database connection. |
285 | 300 | Future<void> dispose() async { |
286 | | - if (!_isInitialized) return; |
| 301 | + // Only attempt to dispose if initialization has been started. |
| 302 | + if (_initCompleter == null) { |
| 303 | + _log.info('Dispose called, but dependencies were never initialized.'); |
| 304 | + return; |
| 305 | + } |
| 306 | + |
| 307 | + _log.info('Disposing application dependencies...'); |
287 | 308 | await _mongoDbConnectionManager.close(); |
288 | 309 | tokenBlacklistService.dispose(); |
289 | 310 | rateLimitService.dispose(); |
290 | 311 | countryQueryService.dispose(); // Dispose the new service |
291 | | - _isInitialized = false; |
292 | | - _log.info('Application dependencies disposed.'); |
| 312 | + |
| 313 | + // Reset the completer to allow for re-initialization (e.g., in tests). |
| 314 | + _initCompleter = null; |
| 315 | + _log.info('Application dependencies disposed and state reset.'); |
293 | 316 | } |
294 | 317 | } |
0 commit comments