Skip to content

Commit 5712bf9

Browse files
committed
refactor(api): centralize DI in root middleware
Refactors the dependency injection mechanism to be handled entirely within the root middleware (`routes/_middleware.dart`). - All services and repositories are now initialized asynchronously within the middleware for each request. - Uses the `DatabaseConnectionManager` singleton to ensure a single, shared database connection. - This approach is more robust and aligned with Dart Frog's lifecycle, removing the reliance on a custom `run` function and the `DependencyContainer` singleton.
1 parent 78e1030 commit 5712bf9

File tree

1 file changed

+154
-81
lines changed

1 file changed

+154
-81
lines changed

routes/_middleware.dart

Lines changed: 154 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import 'package:dart_frog/dart_frog.dart';
2-
import 'package:ht_api/src/config/dependency_container.dart';
2+
import 'package:ht_api/src/config/database_connection.dart';
33
import 'package:ht_api/src/middlewares/error_handler.dart';
44
import 'package:ht_api/src/rbac/permission_service.dart';
55
import 'package:ht_api/src/services/auth_service.dart';
66
import 'package:ht_api/src/services/auth_token_service.dart';
77
import 'package:ht_api/src/services/dashboard_summary_service.dart';
8+
import 'package:ht_api/src/services/database_seeding_service.dart';
9+
import 'package:ht_api/src/services/default_user_preference_limit_service.dart';
10+
import 'package:ht_api/src/services/jwt_auth_token_service.dart';
811
import 'package:ht_api/src/services/token_blacklist_service.dart';
912
import 'package:ht_api/src/services/user_preference_limit_service.dart';
1013
import 'package:ht_api/src/services/verification_code_storage_service.dart';
14+
import 'package:ht_data_client/ht_data_client.dart';
15+
import 'package:ht_data_postgres/ht_data_postgres.dart';
1116
import 'package:ht_data_repository/ht_data_repository.dart';
17+
import 'package:ht_email_inmemory/ht_email_inmemory.dart';
1218
import 'package:ht_email_repository/ht_email_repository.dart';
1319
import 'package:ht_shared/ht_shared.dart';
1420
import 'package:logging/logging.dart';
21+
import 'package:postgres/postgres.dart';
1522
import 'package:uuid/uuid.dart';
1623

1724
// --- Request ID Wrapper ---
@@ -59,6 +66,24 @@ class RequestId {
5966
// --- Middleware Definition ---
6067
final _log = Logger('RootMiddleware');
6168

69+
/// Creates a data repository for a given type [T].
70+
HtDataRepository<T> _createRepository<T>({
71+
required Connection connection,
72+
required String tableName,
73+
required FromJson<T> fromJson,
74+
required ToJson<T> toJson,
75+
}) {
76+
return HtDataRepository<T>(
77+
dataClient: HtDataPostgresClient<T>(
78+
connection: connection,
79+
tableName: tableName,
80+
fromJson: fromJson,
81+
toJson: toJson,
82+
log: _log,
83+
),
84+
);
85+
}
86+
6287
Handler middleware(Handler handler) {
6388
// This is the root middleware for the entire API. It's responsible for
6489
// providing all shared dependencies to the request context.
@@ -81,85 +106,133 @@ Handler middleware(Handler handler) {
81106
return innerHandler(context.provide<RequestId>(() => requestId));
82107
};
83108
})
84-
// --- Dependency Providers ---
85-
// These providers inject all repositories and services into the context.
86-
// They read from the `DependencyContainer` which was populated at startup.
87-
// This is the first set of middleware to run for any request.
88-
.use(provider<Uuid>((_) => const Uuid()))
89-
.use(
90-
provider<HtDataRepository<Headline>>(
91-
(_) => DependencyContainer.instance.headlineRepository,
92-
),
93-
)
94-
.use(
95-
provider<HtDataRepository<Category>>(
96-
(_) => DependencyContainer.instance.categoryRepository,
97-
),
98-
)
99-
.use(
100-
provider<HtDataRepository<Source>>(
101-
(_) => DependencyContainer.instance.sourceRepository,
102-
),
103-
)
104-
.use(
105-
provider<HtDataRepository<Country>>(
106-
(_) => DependencyContainer.instance.countryRepository,
107-
),
108-
)
109-
.use(
110-
provider<HtDataRepository<User>>(
111-
(_) => DependencyContainer.instance.userRepository,
112-
),
113-
)
114-
.use(
115-
provider<HtDataRepository<UserAppSettings>>(
116-
(_) => DependencyContainer.instance.userAppSettingsRepository,
117-
),
118-
)
119-
.use(
120-
provider<HtDataRepository<UserContentPreferences>>(
121-
(_) => DependencyContainer.instance.userContentPreferencesRepository,
122-
),
123-
)
124-
.use(
125-
provider<HtDataRepository<AppConfig>>(
126-
(_) => DependencyContainer.instance.appConfigRepository,
127-
),
128-
)
129-
.use(
130-
provider<HtEmailRepository>(
131-
(_) => DependencyContainer.instance.emailRepository,
132-
),
133-
)
134-
.use(
135-
provider<TokenBlacklistService>(
136-
(_) => DependencyContainer.instance.tokenBlacklistService,
137-
),
138-
)
139-
.use(
140-
provider<AuthTokenService>(
141-
(_) => DependencyContainer.instance.authTokenService,
142-
),
143-
)
144-
.use(
145-
provider<VerificationCodeStorageService>(
146-
(_) => DependencyContainer.instance.verificationCodeStorageService,
147-
),
148-
)
149-
.use(provider<AuthService>((_) => DependencyContainer.instance.authService))
150-
.use(
151-
provider<DashboardSummaryService>(
152-
(_) => DependencyContainer.instance.dashboardSummaryService,
153-
),
154-
)
155-
.use(
156-
provider<PermissionService>(
157-
(_) => DependencyContainer.instance.permissionService,
158-
),
159-
)
160-
.use(
161-
provider<UserPreferenceLimitService>(
162-
(_) => DependencyContainer.instance.userPreferenceLimitService,
163-
),
109+
// --- Dependency Provider ---
110+
// This is the outermost middleware. It runs once per request, before any
111+
// other middleware. It's responsible for initializing and providing all
112+
// dependencies for the request.
113+
.use((handler) {
114+
return (context) async {
115+
// 1. Ensure the database connection is initialized.
116+
await DatabaseConnectionManager.instance.init();
117+
final connection = await DatabaseConnectionManager.instance.connection;
118+
119+
// 2. Run database seeding (idempotent).
120+
final seedingService = DatabaseSeedingService(
121+
connection: connection,
122+
log: _log,
123+
);
124+
await seedingService.createTables();
125+
await seedingService.seedGlobalFixtureData();
126+
await seedingService.seedInitialAdminAndConfig();
127+
128+
// 3. Initialize Repositories.
129+
final headlineRepository = _createRepository<Headline>(
130+
connection: connection,
131+
tableName: 'headlines',
132+
fromJson: Headline.fromJson,
133+
toJson: (h) => h.toJson(),
134+
);
135+
final categoryRepository = _createRepository<Category>(
136+
connection: connection,
137+
tableName: 'categories',
138+
fromJson: Category.fromJson,
139+
toJson: (c) => c.toJson(),
140+
);
141+
final sourceRepository = _createRepository<Source>(
142+
connection: connection,
143+
tableName: 'sources',
144+
fromJson: Source.fromJson,
145+
toJson: (s) => s.toJson(),
146+
);
147+
final countryRepository = _createRepository<Country>(
148+
connection: connection,
149+
tableName: 'countries',
150+
fromJson: Country.fromJson,
151+
toJson: (c) => c.toJson(),
152+
);
153+
final userRepository = _createRepository<User>(
154+
connection: connection,
155+
tableName: 'users',
156+
fromJson: User.fromJson,
157+
toJson: (u) => u.toJson(),
158+
);
159+
final userAppSettingsRepository = _createRepository<UserAppSettings>(
160+
connection: connection,
161+
tableName: 'user_app_settings',
162+
fromJson: UserAppSettings.fromJson,
163+
toJson: (s) => s.toJson(),
164164
);
165+
final userContentPreferencesRepository =
166+
_createRepository<UserContentPreferences>(
167+
connection: connection,
168+
tableName: 'user_content_preferences',
169+
fromJson: UserContentPreferences.fromJson,
170+
toJson: (p) => p.toJson(),
171+
);
172+
final appConfigRepository = _createRepository<AppConfig>(
173+
connection: connection,
174+
tableName: 'app_config',
175+
fromJson: AppConfig.fromJson,
176+
toJson: (c) => c.toJson(),
177+
);
178+
179+
// 4. Initialize Services.
180+
const emailRepository = HtEmailRepository(
181+
emailClient: HtEmailInMemoryClient(),
182+
);
183+
final tokenBlacklistService = InMemoryTokenBlacklistService();
184+
final AuthTokenService authTokenService = JwtAuthTokenService(
185+
userRepository: userRepository,
186+
blacklistService: tokenBlacklistService,
187+
uuidGenerator: const Uuid(),
188+
);
189+
final verificationCodeStorageService =
190+
InMemoryVerificationCodeStorageService();
191+
final authService = AuthService(
192+
userRepository: userRepository,
193+
authTokenService: authTokenService,
194+
verificationCodeStorageService: verificationCodeStorageService,
195+
emailRepository: emailRepository,
196+
userAppSettingsRepository: userAppSettingsRepository,
197+
userContentPreferencesRepository: userContentPreferencesRepository,
198+
uuidGenerator: const Uuid(),
199+
);
200+
final dashboardSummaryService = DashboardSummaryService(
201+
headlineRepository: headlineRepository,
202+
categoryRepository: categoryRepository,
203+
sourceRepository: sourceRepository,
204+
);
205+
const permissionService = PermissionService();
206+
final UserPreferenceLimitService userPreferenceLimitService =
207+
DefaultUserPreferenceLimitService(
208+
appConfigRepository: appConfigRepository,
209+
);
210+
211+
// 5. Provide all dependencies to the inner handler.
212+
return handler
213+
.use(provider<Uuid>((_) => const Uuid()))
214+
.use(provider<HtDataRepository<Headline>>((_) => headlineRepository))
215+
.use(provider<HtDataRepository<Category>>((_) => categoryRepository))
216+
.use(provider<HtDataRepository<Source>>((_) => sourceRepository))
217+
.use(provider<HtDataRepository<Country>>((_) => countryRepository))
218+
.use(provider<HtDataRepository<User>>((_) => userRepository))
219+
.use(provider<HtDataRepository<UserAppSettings>>(
220+
(_) => userAppSettingsRepository))
221+
.use(provider<HtDataRepository<UserContentPreferences>>(
222+
(_) => userContentPreferencesRepository))
223+
.use(provider<HtDataRepository<AppConfig>>((_) => appConfigRepository))
224+
.use(provider<HtEmailRepository>((_) => emailRepository))
225+
.use(provider<TokenBlacklistService>((_) => tokenBlacklistService))
226+
.use(provider<AuthTokenService>((_) => authTokenService))
227+
.use(provider<VerificationCodeStorageService>(
228+
(_) => verificationCodeStorageService))
229+
.use(provider<AuthService>((_) => authService))
230+
.use(provider<DashboardSummaryService>((_) => dashboardSummaryService))
231+
.use(provider<PermissionService>((_) => permissionService))
232+
.use(provider<UserPreferenceLimitService>(
233+
(_) => userPreferenceLimitService))
234+
.call(context);
235+
};
236+
});
237+
}
165238
}

0 commit comments

Comments
 (0)