Skip to content

Commit e7d0d8d

Browse files
committed
fix(config): prevent state corruption on failed dependency init
1 parent 96912c2 commit e7d0d8d

File tree

1 file changed

+137
-122
lines changed

1 file changed

+137
-122
lines changed

lib/src/config/app_dependencies.dart

Lines changed: 137 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class AppDependencies {
3232
static AppDependencies get instance => _instance;
3333

3434
bool _isInitialized = false;
35+
Exception? _initializationError;
3536
final _log = Logger('AppDependencies');
3637

3738
// --- Late-initialized fields for all dependencies ---
@@ -64,132 +65,146 @@ class AppDependencies {
6465
///
6566
/// This method is idempotent; it will only run the initialization logic once.
6667
Future<void> init() async {
68+
// If initialization previously failed, re-throw the original error.
69+
if (_initializationError != null) {
70+
throw _initializationError!;
71+
}
72+
6773
if (_isInitialized) return;
6874

6975
_log.info('Initializing application dependencies...');
7076

71-
// 1. Initialize Database Connection
72-
_mongoDbConnectionManager = MongoDbConnectionManager();
73-
await _mongoDbConnectionManager.init(EnvironmentConfig.databaseUrl);
74-
_log.info('MongoDB connection established.');
75-
76-
// 2. Seed Database
77-
final seedingService = DatabaseSeedingService(
78-
db: _mongoDbConnectionManager.db,
79-
log: Logger('DatabaseSeedingService'),
80-
);
81-
await seedingService.seedInitialData();
82-
_log.info('Database seeding complete.');
83-
84-
// 3. Initialize Data Clients (MongoDB implementation)
85-
final headlineClient = HtDataMongodb<Headline>(
86-
connectionManager: _mongoDbConnectionManager,
87-
modelName: 'headlines',
88-
fromJson: Headline.fromJson,
89-
toJson: (item) => item.toJson(),
90-
logger: Logger('HtDataMongodb<Headline>'),
91-
);
92-
final topicClient = HtDataMongodb<Topic>(
93-
connectionManager: _mongoDbConnectionManager,
94-
modelName: 'topics',
95-
fromJson: Topic.fromJson,
96-
toJson: (item) => item.toJson(),
97-
logger: Logger('HtDataMongodb<Topic>'),
98-
);
99-
final sourceClient = HtDataMongodb<Source>(
100-
connectionManager: _mongoDbConnectionManager,
101-
modelName: 'sources',
102-
fromJson: Source.fromJson,
103-
toJson: (item) => item.toJson(),
104-
logger: Logger('HtDataMongodb<Source>'),
105-
);
106-
final countryClient = HtDataMongodb<Country>(
107-
connectionManager: _mongoDbConnectionManager,
108-
modelName: 'countries',
109-
fromJson: Country.fromJson,
110-
toJson: (item) => item.toJson(),
111-
logger: Logger('HtDataMongodb<Country>'),
112-
);
113-
final userClient = HtDataMongodb<User>(
114-
connectionManager: _mongoDbConnectionManager,
115-
modelName: 'users',
116-
fromJson: User.fromJson,
117-
toJson: (item) => item.toJson(),
118-
logger: Logger('HtDataMongodb<User>'),
119-
);
120-
final userAppSettingsClient = HtDataMongodb<UserAppSettings>(
121-
connectionManager: _mongoDbConnectionManager,
122-
modelName: 'user_app_settings',
123-
fromJson: UserAppSettings.fromJson,
124-
toJson: (item) => item.toJson(),
125-
logger: Logger('HtDataMongodb<UserAppSettings>'),
126-
);
127-
final userContentPreferencesClient = HtDataMongodb<UserContentPreferences>(
128-
connectionManager: _mongoDbConnectionManager,
129-
modelName: 'user_content_preferences',
130-
fromJson: UserContentPreferences.fromJson,
131-
toJson: (item) => item.toJson(),
132-
logger: Logger('HtDataMongodb<UserContentPreferences>'),
133-
);
134-
final remoteConfigClient = HtDataMongodb<RemoteConfig>(
135-
connectionManager: _mongoDbConnectionManager,
136-
modelName: 'remote_configs',
137-
fromJson: RemoteConfig.fromJson,
138-
toJson: (item) => item.toJson(),
139-
logger: Logger('HtDataMongodb<RemoteConfig>'),
140-
);
141-
142-
// 4. Initialize Repositories
143-
headlineRepository = HtDataRepository(dataClient: headlineClient);
144-
topicRepository = HtDataRepository(dataClient: topicClient);
145-
sourceRepository = HtDataRepository(dataClient: sourceClient);
146-
countryRepository = HtDataRepository(dataClient: countryClient);
147-
userRepository = HtDataRepository(dataClient: userClient);
148-
userAppSettingsRepository =
149-
HtDataRepository(dataClient: userAppSettingsClient);
150-
userContentPreferencesRepository =
151-
HtDataRepository(dataClient: userContentPreferencesClient);
152-
remoteConfigRepository = HtDataRepository(dataClient: remoteConfigClient);
153-
154-
final emailClient = HtEmailInMemoryClient(
155-
logger: Logger('HtEmailInMemoryClient'),
156-
);
157-
emailRepository = HtEmailRepository(emailClient: emailClient);
158-
159-
// 5. Initialize Services
160-
tokenBlacklistService = InMemoryTokenBlacklistService(
161-
log: Logger('InMemoryTokenBlacklistService'),
162-
);
163-
authTokenService = JwtAuthTokenService(
164-
userRepository: userRepository,
165-
blacklistService: tokenBlacklistService,
166-
uuidGenerator: const Uuid(),
167-
log: Logger('JwtAuthTokenService'),
168-
);
169-
verificationCodeStorageService = InMemoryVerificationCodeStorageService();
170-
authService = AuthService(
171-
userRepository: userRepository,
172-
authTokenService: authTokenService,
173-
verificationCodeStorageService: verificationCodeStorageService,
174-
emailRepository: emailRepository,
175-
userAppSettingsRepository: userAppSettingsRepository,
176-
userContentPreferencesRepository: userContentPreferencesRepository,
177-
uuidGenerator: const Uuid(),
178-
log: Logger('AuthService'),
179-
);
180-
dashboardSummaryService = DashboardSummaryService(
181-
headlineRepository: headlineRepository,
182-
topicRepository: topicRepository,
183-
sourceRepository: sourceRepository,
184-
);
185-
permissionService = const PermissionService();
186-
userPreferenceLimitService = DefaultUserPreferenceLimitService(
187-
remoteConfigRepository: remoteConfigRepository,
188-
log: Logger('DefaultUserPreferenceLimitService'),
189-
);
190-
191-
_isInitialized = true;
192-
_log.info('Application dependencies initialized successfully.');
77+
try {
78+
// 1. Initialize Database Connection
79+
_mongoDbConnectionManager = MongoDbConnectionManager();
80+
await _mongoDbConnectionManager.init(EnvironmentConfig.databaseUrl);
81+
_log.info('MongoDB connection established.');
82+
83+
// 2. Seed Database
84+
final seedingService = DatabaseSeedingService(
85+
db: _mongoDbConnectionManager.db,
86+
log: Logger('DatabaseSeedingService'),
87+
);
88+
await seedingService.seedInitialData();
89+
_log.info('Database seeding complete.');
90+
91+
// 3. Initialize Data Clients (MongoDB implementation)
92+
final headlineClient = HtDataMongodb<Headline>(
93+
connectionManager: _mongoDbConnectionManager,
94+
modelName: 'headlines',
95+
fromJson: Headline.fromJson,
96+
toJson: (item) => item.toJson(),
97+
logger: Logger('HtDataMongodb<Headline>'),
98+
);
99+
final topicClient = HtDataMongodb<Topic>(
100+
connectionManager: _mongoDbConnectionManager,
101+
modelName: 'topics',
102+
fromJson: Topic.fromJson,
103+
toJson: (item) => item.toJson(),
104+
logger: Logger('HtDataMongodb<Topic>'),
105+
);
106+
final sourceClient = HtDataMongodb<Source>(
107+
connectionManager: _mongoDbConnectionManager,
108+
modelName: 'sources',
109+
fromJson: Source.fromJson,
110+
toJson: (item) => item.toJson(),
111+
logger: Logger('HtDataMongodb<Source>'),
112+
);
113+
final countryClient = HtDataMongodb<Country>(
114+
connectionManager: _mongoDbConnectionManager,
115+
modelName: 'countries',
116+
fromJson: Country.fromJson,
117+
toJson: (item) => item.toJson(),
118+
logger: Logger('HtDataMongodb<Country>'),
119+
);
120+
final userClient = HtDataMongodb<User>(
121+
connectionManager: _mongoDbConnectionManager,
122+
modelName: 'users',
123+
fromJson: User.fromJson,
124+
toJson: (item) => item.toJson(),
125+
logger: Logger('HtDataMongodb<User>'),
126+
);
127+
final userAppSettingsClient = HtDataMongodb<UserAppSettings>(
128+
connectionManager: _mongoDbConnectionManager,
129+
modelName: 'user_app_settings',
130+
fromJson: UserAppSettings.fromJson,
131+
toJson: (item) => item.toJson(),
132+
logger: Logger('HtDataMongodb<UserAppSettings>'),
133+
);
134+
final userContentPreferencesClient =
135+
HtDataMongodb<UserContentPreferences>(
136+
connectionManager: _mongoDbConnectionManager,
137+
modelName: 'user_content_preferences',
138+
fromJson: UserContentPreferences.fromJson,
139+
toJson: (item) => item.toJson(),
140+
logger: Logger('HtDataMongodb<UserContentPreferences>'),
141+
);
142+
final remoteConfigClient = HtDataMongodb<RemoteConfig>(
143+
connectionManager: _mongoDbConnectionManager,
144+
modelName: 'remote_configs',
145+
fromJson: RemoteConfig.fromJson,
146+
toJson: (item) => item.toJson(),
147+
logger: Logger('HtDataMongodb<RemoteConfig>'),
148+
);
149+
150+
// 4. Initialize Repositories
151+
headlineRepository = HtDataRepository(dataClient: headlineClient);
152+
topicRepository = HtDataRepository(dataClient: topicClient);
153+
sourceRepository = HtDataRepository(dataClient: sourceClient);
154+
countryRepository = HtDataRepository(dataClient: countryClient);
155+
userRepository = HtDataRepository(dataClient: userClient);
156+
userAppSettingsRepository =
157+
HtDataRepository(dataClient: userAppSettingsClient);
158+
userContentPreferencesRepository =
159+
HtDataRepository(dataClient: userContentPreferencesClient);
160+
remoteConfigRepository =
161+
HtDataRepository(dataClient: remoteConfigClient);
162+
163+
final emailClient = HtEmailInMemoryClient(
164+
logger: Logger('HtEmailInMemoryClient'),
165+
);
166+
emailRepository = HtEmailRepository(emailClient: emailClient);
167+
168+
// 5. Initialize Services
169+
tokenBlacklistService = InMemoryTokenBlacklistService(
170+
log: Logger('InMemoryTokenBlacklistService'),
171+
);
172+
authTokenService = JwtAuthTokenService(
173+
userRepository: userRepository,
174+
blacklistService: tokenBlacklistService,
175+
uuidGenerator: const Uuid(),
176+
log: Logger('JwtAuthTokenService'),
177+
);
178+
verificationCodeStorageService =
179+
InMemoryVerificationCodeStorageService();
180+
authService = AuthService(
181+
userRepository: userRepository,
182+
authTokenService: authTokenService,
183+
verificationCodeStorageService: verificationCodeStorageService,
184+
emailRepository: emailRepository,
185+
userAppSettingsRepository: userAppSettingsRepository,
186+
userContentPreferencesRepository: userContentPreferencesRepository,
187+
uuidGenerator: const Uuid(),
188+
log: Logger('AuthService'),
189+
);
190+
dashboardSummaryService = DashboardSummaryService(
191+
headlineRepository: headlineRepository,
192+
topicRepository: topicRepository,
193+
sourceRepository: sourceRepository,
194+
);
195+
permissionService = const PermissionService();
196+
userPreferenceLimitService = DefaultUserPreferenceLimitService(
197+
remoteConfigRepository: remoteConfigRepository,
198+
log: Logger('DefaultUserPreferenceLimitService'),
199+
);
200+
201+
_isInitialized = true;
202+
_log.info('Application dependencies initialized successfully.');
203+
} on Exception catch (e) {
204+
_log.severe('Failed to initialize application dependencies', e);
205+
_initializationError = e;
206+
rethrow;
207+
}
193208
}
194209

195210
/// Disposes of resources, such as closing the database connection.

0 commit comments

Comments
 (0)