251 lines
7.5 KiB
TypeScript
251 lines
7.5 KiB
TypeScript
/**
|
|
* Dependency Injection Container
|
|
*
|
|
* Initializes all in-memory repositories and provides accessor functions.
|
|
* Allows easy swapping to persistent repositories later.
|
|
*/
|
|
|
|
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
|
|
import { League } from '@gridpilot/racing/domain/entities/League';
|
|
import { Race } from '@gridpilot/racing/domain/entities/Race';
|
|
import { Result } from '@gridpilot/racing/domain/entities/Result';
|
|
import { Standing } from '@gridpilot/racing/domain/entities/Standing';
|
|
|
|
import type { IDriverRepository } from '@gridpilot/racing/domain/repositories/IDriverRepository';
|
|
import type { ILeagueRepository } from '@gridpilot/racing/domain/repositories/ILeagueRepository';
|
|
import type { IRaceRepository } from '@gridpilot/racing/domain/repositories/IRaceRepository';
|
|
import type { IResultRepository } from '@gridpilot/racing/domain/repositories/IResultRepository';
|
|
import type { IStandingRepository } from '@gridpilot/racing/domain/repositories/IStandingRepository';
|
|
import type { IFeedRepository } from '@gridpilot/social/domain/repositories/IFeedRepository';
|
|
import type { ISocialGraphRepository } from '@gridpilot/social/domain/repositories/ISocialGraphRepository';
|
|
|
|
import { InMemoryDriverRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryDriverRepository';
|
|
import { InMemoryLeagueRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryLeagueRepository';
|
|
import { InMemoryRaceRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryRaceRepository';
|
|
import { InMemoryResultRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryResultRepository';
|
|
import { InMemoryStandingRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryStandingRepository';
|
|
import { createStaticRacingSeed, type RacingSeedData } from '@gridpilot/testing-support';
|
|
import {
|
|
InMemoryFeedRepository,
|
|
InMemorySocialGraphRepository,
|
|
} from '@gridpilot/social/infrastructure/inmemory/InMemorySocialAndFeed';
|
|
|
|
/**
|
|
* Seed data for development
|
|
*/
|
|
/**
|
|
* Driver statistics and ranking data
|
|
*/
|
|
export interface DriverStats {
|
|
driverId: string;
|
|
rating: number;
|
|
totalRaces: number;
|
|
wins: number;
|
|
podiums: number;
|
|
dnfs: number;
|
|
avgFinish: number;
|
|
bestFinish: number;
|
|
worstFinish: number;
|
|
consistency: number;
|
|
overallRank: number;
|
|
percentile: number;
|
|
}
|
|
|
|
/**
|
|
* Mock driver stats with calculated rankings
|
|
*/
|
|
const driverStats: Record<string, DriverStats> = {};
|
|
|
|
function createSeedData(): RacingSeedData {
|
|
const seed = createStaticRacingSeed(42);
|
|
const { drivers } = seed;
|
|
|
|
drivers.forEach((driver, index) => {
|
|
const totalRaces = 40 + index * 5;
|
|
const wins = Math.max(0, Math.floor(totalRaces * 0.2) - index);
|
|
const podiums = Math.max(wins * 2, 0);
|
|
const dnfs = Math.max(0, Math.floor(index / 2));
|
|
const rating = 1500 + index * 25;
|
|
|
|
driverStats[driver.id] = {
|
|
driverId: driver.id,
|
|
rating,
|
|
totalRaces,
|
|
wins,
|
|
podiums,
|
|
dnfs,
|
|
avgFinish: 4,
|
|
bestFinish: 1,
|
|
worstFinish: 20,
|
|
consistency: 80,
|
|
overallRank: index + 1,
|
|
percentile: Math.max(0, 100 - index),
|
|
};
|
|
});
|
|
|
|
return seed;
|
|
}
|
|
|
|
/**
|
|
* DI Container class
|
|
*/
|
|
class DIContainer {
|
|
private static instance: DIContainer;
|
|
|
|
private _driverRepository: IDriverRepository;
|
|
private _leagueRepository: ILeagueRepository;
|
|
private _raceRepository: IRaceRepository;
|
|
private _resultRepository: IResultRepository;
|
|
private _standingRepository: IStandingRepository;
|
|
private _feedRepository: IFeedRepository;
|
|
private _socialRepository: ISocialGraphRepository;
|
|
|
|
private constructor() {
|
|
// Create seed data
|
|
const seedData = createSeedData();
|
|
|
|
// Initialize repositories with seed data
|
|
this._driverRepository = new InMemoryDriverRepository(seedData.drivers);
|
|
this._leagueRepository = new InMemoryLeagueRepository(seedData.leagues);
|
|
this._raceRepository = new InMemoryRaceRepository(seedData.races);
|
|
|
|
// Result repository needs race repository for league-based queries
|
|
this._resultRepository = new InMemoryResultRepository(
|
|
seedData.results,
|
|
this._raceRepository
|
|
);
|
|
|
|
// Standing repository needs all three for recalculation
|
|
this._standingRepository = new InMemoryStandingRepository(
|
|
seedData.standings,
|
|
this._resultRepository,
|
|
this._raceRepository,
|
|
this._leagueRepository
|
|
);
|
|
|
|
// Social and feed adapters backed by static seed
|
|
this._feedRepository = new InMemoryFeedRepository(seedData);
|
|
this._socialRepository = new InMemorySocialGraphRepository(seedData);
|
|
}
|
|
|
|
/**
|
|
* Get singleton instance
|
|
*/
|
|
static getInstance(): DIContainer {
|
|
if (!DIContainer.instance) {
|
|
DIContainer.instance = new DIContainer();
|
|
}
|
|
return DIContainer.instance;
|
|
}
|
|
|
|
/**
|
|
* Reset the container (useful for testing)
|
|
*/
|
|
static reset(): void {
|
|
DIContainer.instance = new DIContainer();
|
|
}
|
|
|
|
/**
|
|
* Repository getters
|
|
*/
|
|
get driverRepository(): IDriverRepository {
|
|
return this._driverRepository;
|
|
}
|
|
|
|
get leagueRepository(): ILeagueRepository {
|
|
return this._leagueRepository;
|
|
}
|
|
|
|
get raceRepository(): IRaceRepository {
|
|
return this._raceRepository;
|
|
}
|
|
|
|
get resultRepository(): IResultRepository {
|
|
return this._resultRepository;
|
|
}
|
|
|
|
get standingRepository(): IStandingRepository {
|
|
return this._standingRepository;
|
|
}
|
|
|
|
get feedRepository(): IFeedRepository {
|
|
return this._feedRepository;
|
|
}
|
|
|
|
get socialRepository(): ISocialGraphRepository {
|
|
return this._socialRepository;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Exported accessor functions
|
|
*/
|
|
export function getDriverRepository(): IDriverRepository {
|
|
return DIContainer.getInstance().driverRepository;
|
|
}
|
|
|
|
export function getLeagueRepository(): ILeagueRepository {
|
|
return DIContainer.getInstance().leagueRepository;
|
|
}
|
|
|
|
export function getRaceRepository(): IRaceRepository {
|
|
return DIContainer.getInstance().raceRepository;
|
|
}
|
|
|
|
export function getResultRepository(): IResultRepository {
|
|
return DIContainer.getInstance().resultRepository;
|
|
}
|
|
|
|
export function getStandingRepository(): IStandingRepository {
|
|
return DIContainer.getInstance().standingRepository;
|
|
}
|
|
|
|
export function getFeedRepository(): IFeedRepository {
|
|
return DIContainer.getInstance().feedRepository;
|
|
}
|
|
|
|
export function getSocialRepository(): ISocialGraphRepository {
|
|
return DIContainer.getInstance().socialRepository;
|
|
}
|
|
|
|
/**
|
|
* Reset function for testing
|
|
*/
|
|
export function resetContainer(): void {
|
|
DIContainer.reset();
|
|
}
|
|
|
|
/**
|
|
* Get driver statistics and rankings
|
|
*/
|
|
export function getDriverStats(driverId: string): DriverStats | null {
|
|
return driverStats[driverId] || null;
|
|
}
|
|
|
|
/**
|
|
* Get all driver rankings sorted by rating
|
|
*/
|
|
export function getAllDriverRankings(): DriverStats[] {
|
|
return Object.values(driverStats).sort((a, b) => b.rating - a.rating);
|
|
}
|
|
|
|
/**
|
|
* Get league-specific rankings for a driver
|
|
*/
|
|
export function getLeagueRankings(driverId: string, leagueId: string): {
|
|
rank: number;
|
|
totalDrivers: number;
|
|
percentile: number;
|
|
} {
|
|
// Mock league rankings (in production, calculate from actual league membership)
|
|
const mockLeagueRanks: Record<string, Record<string, any>> = {
|
|
'league-1': {
|
|
'driver-1': { rank: 1, totalDrivers: 12, percentile: 92 },
|
|
'driver-2': { rank: 2, totalDrivers: 12, percentile: 84 },
|
|
'driver-3': { rank: 4, totalDrivers: 12, percentile: 67 },
|
|
'driver-4': { rank: 5, totalDrivers: 12, percentile: 58 }
|
|
}
|
|
};
|
|
|
|
return mockLeagueRanks[leagueId]?.[driverId] || { rank: 0, totalDrivers: 0, percentile: 0 };
|
|
} |