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

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

// 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

// 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

// 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

// 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

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

// 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

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

const { leagueService } = useServices();

After

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