Salve ponownie, legioniście! Teraz poznamy tajniki organizacji legionu w naszym Imperium. Każdy doświadczony konsul wie, że dobra organizacja i jasny podział obowiązków to klucz do sukcesu każdej kampanii!
W NestJS moduły to jak różne struktury Imperium - każda ma swoje przeznaczenie, ale wszystkie współpracują ze sobą, tworząc sprawnie działającą machinerię imperialną!
1// legion.module.ts - struktura legionu
2import { Module } from '@nestjs/common';
3import { LegionController } from './legion.controller';
4import { LegionService } from './legion.service';
5import { LegionaryValidator } from './legionary.validator';
6
7@Module({
8 controllers: [LegionController], // Centurionowie struktur
9 providers: [LegionService, LegionaryValidator], // Specjaliści i narzędzia
10 exports: [LegionService], // Co udostępniamy innym strukturom
11})
12export class LegionModule {}Czasem jedna struktura Imperium potrzebuje pomocy od innej:
1// tributes.module.ts - struktura trybutów
2@Module({
3 imports: [LegionModule], // Importujemy strukturę legionu
4 controllers: [TributesController],
5 providers: [
6 TributesService,
7 TributesMapper,
8 TributesGuard,
9 ],
10 exports: [TributesService],
11})
12export class TributesModule {}
13
14// W serwisie trybutów możemy używać serwisu legionu
15@Injectable()
16export class TributesService {
17 constructor(
18 private readonly legionService: LegionService, // Dostępne dzięki import + export
19 ) {}
20
21 async assignTributeCollection(provinceId: string) {
22 const availableLegionaries = await this.legionService.getAvailableLegionaries();
23 return this.legionService.assignMission(availableLegionaries[0], provinceId);
24 }
25}Niektóre usługi potrzebne są w całym Imperium - jak system komunikacji:
1// communication.module.ts
2@Global() // Dostępne wszędzie bez importowania
3@Module({
4 providers: [
5 SignalService,
6 MessageService,
7 EmergencyService,
8 ],
9 exports: [SignalService, MessageService, EmergencyService],
10})
11export class CommunicationModule {}DI w NestJS to jak hierarchia i podział obowiązków w legionie - każdy wie, od kogo brać rozkazy i komu przekazywać informacje.
1// Serwis inżynieryjny - specjalista od tras
2@Injectable()
3export class EngineeringService {
4 private currentRoute: Route;
5
6 calculateRoute(from: Coordinates, to: Coordinates): Route {
7 // Skomplikowane obliczenia trasy (Via Appia)
8 return {
9 distance: this.calculateDistance(from, to),
10 estimatedTime: this.calculateTime(from, to),
11 waypoints: this.generateWaypoints(from, to),
12 };
13 }
14}
15
16// Kontroler forum - używa inżyniera
17@Controller('forum')
18export class ForumController {
19 constructor(
20 private readonly engineeringService: EngineeringService, // Automatyczne wstrzyknięcie
21 ) {}
22
23 @Post('march-to')
24 setMarch(@Body() destination: Coordinates) {
25 const route = this.engineeringService.calculateRoute(
26 this.getCurrentPosition(),
27 destination
28 );
29 return {
30 message: 'Campaign route set, consul!',
31 route,
32 };
33 }
34}Czasem potrzebujemy być bardziej precyzyjni w określaniu, czego potrzebujemy:
1// Definiujemy tokeny dla różnych typów konfiguracji
2export const IMPERIUM_CONFIG = 'IMPERIUM_CONFIG';
3export const ARMORY_CONFIG = 'ARMORY_CONFIG';
4
5// W module definiujemy dostawców z tokenami
6@Module({
7 providers: [
8 {
9 provide: IMPERIUM_CONFIG,
10 useValue: {
11 name: 'Roman Imperium',
12 maxMarchSpeed: 50,
13 legionCapacity: 5000,
14 ballistaCount: 32,
15 },
16 },
17 {
18 provide: ARMORY_CONFIG,
19 useValue: {
20 defaultPila: 100,
21 repairTime: 30,
22 range: 500,
23 },
24 },
25 ForumService,
26 ],
27})
28export class ForumModule {}
29
30// W serwisie używamy tokenów do wstrzyknięcia
31@Injectable()
32export class ForumService {
33 constructor(
34 @Inject(IMPERIUM_CONFIG) private imperiumConfig: any,
35 @Inject(ARMORY_CONFIG) private armoryConfig: any,
36 ) {}
37
38 getImperiumInfo() {
39 return {
40 ...this.imperiumConfig,
41 armoryStatus: this.armoryConfig,
42 };
43 }
44}Czasem musimy stworzyć skomplikowane obiekty:
1// Factory do tworzenia różnych typów broni
2const armoryFactory = {
3 provide: 'ARMORY_FACTORY',
4 useFactory: (config: any, logger: Logger) => {
5 return (type: string) => {
6 switch (type) {
7 case 'ballista':
8 return new Ballista(config.ballistaConfig, logger);
9 case 'pilum':
10 return new Pilum(config.pilumConfig, logger);
11 case 'gladius':
12 return new Gladius(config.gladiusConfig);
13 default:
14 throw new Error(`Unknown weapon type: ${type}`);
15 }
16 };
17 },
18 inject: [ARMORY_CONFIG, Logger],
19};
20
21@Module({
22 providers: [armoryFactory],
23 exports: ['ARMORY_FACTORY'],
24})
25export class ArmoryModule {}Niektóre zasoby muszą być załadowane asynchronicznie:
1// Async provider do łączenia z mapą prowincji
2const provinceMapProvider = {
3 provide: 'PROVINCE_MAP_CONNECTION',
4 useFactory: async (): Promise<ProvinceMapConnection> => {
5 const connection = new ProvinceMapConnection();
6 await connection.connect('https://roman-province-maps.com');
7 await connection.authenticate(process.env.IMPERIUM_API_KEY);
8 return connection;
9 },
10};
11
12@Module({
13 providers: [provinceMapProvider],
14 exports: ['PROVINCE_MAP_CONNECTION'],
15})
16export class ProvinceMapModule {}1// Serwis logowania dla każdego żądania osobno
2@Injectable({ scope: Scope.REQUEST })
3export class RequestLoggerService {
4 private requestId: string;
5
6 constructor(@Inject(REQUEST) private request: Request) {
7 this.requestId = Math.random().toString(36).substr(2, 9);
8 }
9
10 log(message: string) {
11 console.log(`[${this.requestId}] ${message}`);
12 }
13}1// Każde wstrzyknięcie tworzy nową instancję
2@Injectable({ scope: Scope.TRANSIENT })
3export class CampaignCalculatorService {
4 calculateDamage(attackPower: number, defense: number): number {
5 // Każda kampania to nowe obliczenia
6 const randomFactor = Math.random() * 0.2 + 0.9; // 90-110% base damage
7 return Math.max(1, Math.floor((attackPower - defense) * randomFactor));
8 }
9}Czasem potrzebujemy tworzyć moduły z różną konfiguracją:
1// Dynamiczny moduł bazy danych
2@Module({})
3export class DatabaseModule {
4 static forRoot(config: DatabaseConfig): DynamicModule {
5 return {
6 module: DatabaseModule,
7 providers: [
8 {
9 provide: 'DATABASE_CONNECTION',
10 useFactory: () => createConnection(config),
11 },
12 DatabaseService,
13 ],
14 exports: ['DATABASE_CONNECTION', DatabaseService],
15 global: true,
16 };
17 }
18
19 static forFeature(entities: any[]): DynamicModule {
20 return {
21 module: DatabaseModule,
22 providers: entities.map(entity => ({
23 provide: `${entity.name}_REPOSITORY`,
24 useFactory: (connection: any) => connection.getRepository(entity),
25 inject: ['DATABASE_CONNECTION'],
26 })),
27 exports: entities.map(entity => `${entity.name}_REPOSITORY`),
28 };
29 }
30}
31
32// Użycie w głównym module
33@Module({
34 imports: [
35 DatabaseModule.forRoot({
36 host: 'localhost',
37 port: 5432,
38 database: 'roman_tributes',
39 }),
40 DatabaseModule.forFeature([Legionary, Tribute, Forum]),
41 ],
42})
43export class AppModule {}1// Główny moduł aplikacji
2@Module({
3 imports: [
4 // Konfiguracja podstawowa
5 ConfigModule.forRoot({
6 isGlobal: true,
7 envFilePath: '.env',
8 }),
9
10 // Baza danych
11 DatabaseModule.forRoot(databaseConfig),
12
13 // Moduły funkcjonalne
14 LegionModule,
15 TributesModule,
16 CampaignModule,
17 EngineeringModule,
18
19 // Moduły wspólne
20 CommunicationModule,
21 LoggingModule,
22 ],
23 controllers: [AppController],
24 providers: [
25 AppService,
26 // Global guards, interceptors, filters
27 {
28 provide: APP_GUARD,
29 useClass: AuthGuard,
30 },
31 {
32 provide: APP_INTERCEPTOR,
33 useClass: LoggingInterceptor,
34 },
35 ],
36})
37export class AppModule implements NestModule {
38 configure(consumer: MiddlewareConsumer) {
39 consumer
40 .apply(ImperiumAuthMiddleware)
41 .forRoutes('*');
42 }
43}Dobra organizacja modułów to fundament skalowalnej aplikacji NestJS. Tak jak dobrze zorganizowany legion potrafi pokonać każdą kampanię, tak przemyślana architektura modułów pomoże ci zbudować aplikację, która będzie rosła razem z twoimi potrzebami!
W następnym module poznamy szczegóły kontrolerów - naszych centurionów, którzy przyjmują rozkazy z zewnątrz i zarządzają operacjami!