March 2025. Project grows. Everything in one folder, everything knows about everything. Scary to change.
Decided to make modules. Each module isolated. Dependencies explicit.
Structure
modules/
auth/
AuthModule.ts
AuthService.ts
AuthController.ts
cms/
CmsModule.ts
CmsService.ts
CmsController.ts
Module - a class that registers its services and controllers.
DI container
class AuthModule {
register(container) {
container.register('authService', AuthService)
container.register('authController', AuthController)
}
}
Service doesn't create dependencies itself. Gets them from container.
class AuthController {
constructor(
private authService: AuthService,
private userService: UserService // injected
) {}
}
What this gave
- Testing: replace dependencies with mocks
- Isolation: module doesn't touch others' internals
- Order: clear where things are
- Plugins: new functionality = new module
Challenges
Circular dependencies. AuthModule needs UserModule, UserModule needs AuthModule. Solution - extract common parts into separate module.
Took a week to refactor. But then adding new things became easier.