Files
gridpilot.gg/apps/website/lib/di/README.md
2026-01-06 19:36:03 +01:00

177 lines
4.5 KiB
Markdown

# Dependency Injection System
This directory contains the new dependency injection system for the GridPilot website, built with InversifyJS.
## Overview
The DI system provides:
- **Centralized dependency management** - All services are registered in modules
- **Type-safe injection** - Compile-time validation of dependencies
- **Easy testing** - Simple mocking via container overrides
- **React integration** - Hooks for component-level injection
- **NestJS-like patterns** - Familiar structure for API developers
## Architecture
```
lib/di/
├── index.ts # Main exports
├── container.ts # Container factory & lifecycle
├── tokens.ts # Symbol-based tokens
├── providers/ # React Context integration
│ └── ContainerProvider.tsx
├── hooks/ # Injection hooks
│ └── useInject.ts
└── modules/ # Domain modules
├── core.module.ts # Logger, error reporter, config
├── api.module.ts # API clients
├── league.module.ts # League services
├── driver.module.ts # Driver services
└── team.module.ts # Team services
```
## Usage
### 1. Setup Root Provider
```tsx
// app/layout.tsx
import { ContainerProvider } from '@/lib/di/providers/ContainerProvider';
export default function RootLayout({ children }) {
return (
<html>
<body>
<ContainerProvider>
{children}
</ContainerProvider>
</body>
</html>
);
}
```
### 2. Inject Services in Hooks
```typescript
// hooks/useLeagueService.ts
import { useInject } from '@/lib/di/hooks/useInject';
import { LEAGUE_SERVICE_TOKEN } from '@/lib/di/tokens';
import { useQuery } from '@tanstack/react-query';
export function useAllLeagues() {
const leagueService = useInject(LEAGUE_SERVICE_TOKEN);
return useQuery({
queryKey: ['allLeagues'],
queryFn: () => leagueService.getAllLeagues(),
});
}
```
### 3. Inject in Components
```typescript
// components/MyComponent.tsx
'use client';
import { useInject } from '@/lib/di/hooks/useInject';
import { LEAGUE_SERVICE_TOKEN } from '@/lib/di/tokens';
export function MyComponent() {
const leagueService = useInject(LEAGUE_SERVICE_TOKEN);
// Use leagueService...
}
```
### 4. Server Components
```typescript
// app/leagues/[id]/page.tsx
import { createContainer } from '@/lib/di/container';
import { LEAGUE_SERVICE_TOKEN } from '@/lib/di/tokens';
export default async function LeaguePage({ params }) {
const container = createContainer();
const leagueService = container.get(LEAGUE_SERVICE_TOKEN);
const league = await leagueService.getLeague(params.id);
return <ClientComponent league={league} />;
}
```
### 5. Testing
```typescript
import { createTestContainer } from '@/lib/di/container';
import { ContainerProvider } from '@/lib/di/providers/ContainerProvider';
import { LEAGUE_SERVICE_TOKEN } from '@/lib/di/tokens';
test('component works', () => {
const mockService = { getData: jest.fn() };
const overrides = new Map([
[LEAGUE_SERVICE_TOKEN, mockService]
]);
const container = createTestContainer(overrides);
render(
<ContainerProvider container={container}>
<MyComponent />
</ContainerProvider>
);
});
```
## Token Naming Convention
```typescript
// Format: DOMAIN_SERVICE_TYPE_TOKEN
export const LEAGUE_SERVICE_TOKEN = Symbol.for('Service.League');
export const LEAGUE_API_CLIENT_TOKEN = Symbol.for('Api.LeagueClient');
export const LOGGER_TOKEN = Symbol.for('Core.Logger');
```
## Module Pattern
```typescript
import { ContainerModule } from 'inversify';
import { Service } from './Service';
import { SERVICE_TOKEN } from '../tokens';
export const DomainModule = new ContainerModule((options) => {
const bind = options.bind;
bind<SERVICE_TOKEN>(SERVICE_TOKEN)
.to(Service)
.inSingletonScope();
});
```
## Migration from Old System
### Before
```typescript
const { leagueService } = useServices();
```
### After
```typescript
const leagueService = useInject(LEAGUE_SERVICE_TOKEN);
```
## Benefits
**Testability** - Easy mocking via container overrides
**Maintainability** - Clear dependency graphs
**Type Safety** - Compile-time validation
**Consistency** - Same patterns as NestJS API
**Performance** - Singleton scope by default
## Next Steps
1. Complete module implementations for all domains
2. Migrate all React-Query hooks to use `useInject()`
3. Update tests to use test containers
4. Remove old `ServiceProvider` and `ServiceFactory`