Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Architektura NestJS - budowa rzymskiego Imperium

Salve ponownie! Teraz gdy już wiesz co to NestJS, czas poznać architekturę naszego Imperium Romanum. Każdy doświadczony konsul wie, że dobra konstrukcja Imperium to podstawa udanej kampanii w niebezpiecznych prowincjach aplikacji enterprise!

Wzorce architektoniczne w NestJS

NestJS opiera się na sprawdzonych wzorcach znanych z innych frameworków i języków programowania. To jak gdyby łączyć najlepsze tradycje budowy Imperium z różnych epok:

1. Model-View-Controller (MVC)

1// Model - struktura naszych danych (trybuty)
2export class Tribute {
3 id: number;
4 name: string;
5 value: number;
6 province: string;
7 collected: boolean;
8}
9
10// Controller - konsul zarządzający operacjami
11@Controller('tributes')
12export class TributesController {
13 constructor(private tributesService: TributesService) {}
14
15 @Get()
16 findAll() {
17 return this.tributesService.findAll();
18 }
19
20 @Post()
21 create(@Body() tribute: CreateTributeDto) {
22 return this.tributesService.create(tribute);
23 }
24}
25
26// Service - doświadczony kolektor trybutów
27@Injectable()
28export class TributesService {
29 private tributes: Tribute[] = [];
30
31 findAll(): Tribute[] {
32 return this.tributes;
33 }
34
35 create(tribute: CreateTributeDto): Tribute {
36 const newTribute = { ...tribute, id: Date.now() };
37 this.tributes.push(newTribute);
38 return newTribute;
39 }
40}

2. Dependency Injection - hierarchia w Imperium

Każdy legionista wie, gdzie ma szukać pomocy i kogo powiadomić o problemach:

1// Konsul potrzebuje inżyniera i kucharza
2@Controller('forum')
3export class ForumController {
4 constructor(
5 private readonly engineeringService: EngineeringService,
6 private readonly cookService: CookService,
7 private readonly weatherService: WeatherService,
8 ) {}
9
10 @Get('status')
11 getForumStatus() {
12 return {
13 position: this.engineeringService.getCurrentPosition(),
14 meal: this.cookService.getTodaysMenu(),
15 weather: this.weatherService.getWeatherReport(),
16 };
17 }
18}

Podstawowe elementy architektury

Moduły - struktury Imperium

Każdy moduł to niezależna struktura Imperium z własnym przeznaczeniem:

1// Moduł legionu - zarządza legionistami
2@Module({
3 imports: [DatabaseModule],
4 controllers: [LegionController],
5 providers: [LegionService, LegionaryValidator],
6 exports: [LegionService], // Udostępnia dla innych modułów
7})
8export class LegionModule {}
9
10// Główny moduł Imperium - łączy wszystkie struktury
11@Module({
12 imports: [
13 LegionModule,
14 TributesModule,
15 EngineeringModule,
16 ArmoryModule,
17 ],
18 controllers: [AppController],
19 providers: [AppService],
20})
21export class AppModule {}

Kontrolery - centurionowie struktur

Kontrolery obsługują żądania HTTP - to centurionowie poszczególnych struktur Imperium:

1@Controller('armory')
2export class ArmoryController {
3 constructor(private armoryService: ArmoryService) {}
4
5 // Sprawdź stan arsenału
6 @Get('status')
7 getArmoryStatus() {
8 return this.armoryService.getInventory();
9 }
10
11 // Dodaj nową broń
12 @Post()
13 addWeapon(@Body() weapon: CreateWeaponDto) {
14 return this.armoryService.addWeapon(weapon);
15 }
16
17 // Użyj broni (atak!)
18 @Post(':id/attack')
19 attackWithWeapon(@Param('id') id: string) {
20 return this.armoryService.useWeapon(+id);
21 }
22
23 // Napraw broń
24 @Patch(':id/repair')
25 repairWeapon(@Param('id') id: string) {
26 return this.armoryService.repairWeapon(+id);
27 }
28}

Serwisy - specjaliści w Imperium

Serwisy zawierają logikę biznesową - to doświadczeni specjaliści rzymscy:

1@Injectable()
2export class EngineeringService {
3 private currentPosition = { lat: 0, lng: 0 };
4 private destination = null;
5
6 getCurrentPosition() {
7 return this.currentPosition;
8 }
9
10 setDestination(coordinates: { lat: number; lng: number }) {
11 this.destination = coordinates;
12 return this.calculateRoute();
13 }
14
15 private calculateRoute() {
16 // Złożona logika inżynieryjna (Via Appia)
17 const distance = this.calculateDistance(this.currentPosition, this.destination);
18 const estimatedTime = distance / 50; // 50 km dziennie marszu
19
20 return {
21 distance,
22 estimatedTime,
23 route: this.generateWaypoints(),
24 };
25 }
26
27 calculateDistance(from: any, to: any): number {
28 // Formuła na odległość imperialną
29 return Math.sqrt(
30 Math.pow(to.lat - from.lat, 2) + Math.pow(to.lng - from.lng, 2)
31 ) * 111; // konwersja na kilometry
32 }
33}

Zaawansowane wzorce

Guards - straże pretoriańskie

Guards kontrolują dostęp - jak straże pretoriańskie sprawdzające, czy obywatele mają prawo wejść do określonych struktur Imperium:

1@Injectable()
2export class ConsulGuard implements CanActivate {
3 canActivate(context: ExecutionContext): boolean {
4 const request = context.switchToHttp().getRequest();
5 const user = request.user;
6
7 // Tylko konsul ma dostęp do niektórych struktur
8 return user && user.rank === 'consul';
9 }
10}
11
12// Użycie strażnika
13@Controller('treasury')
14@UseGuards(ConsulGuard)
15export class TreasuryController {
16 @Get('gold')
17 getImperialTreasury() {
18 return 'Top secret imperial gold: 10000 aureus';
19 }
20}

Interceptors - systemy monitorowania

Interceptors przechwytują i modyfikują żądania/odpowiedzi - jak systemy monitorowania w Imperium:

1@Injectable()
2export class LoggingInterceptor implements NestInterceptor {
3 intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
4 const request = context.switchToHttp().getRequest();
5 const method = request.method;
6 const url = request.url;
7
8 console.log(`⚔️ Imperial action: ${method} ${url}`);
9
10 const start = Date.now();
11 return next.handle().pipe(
12 tap(() => {
13 const duration = Date.now() - start;
14 console.log(`✅ Action completed in ${duration}ms`);
15 }),
16 );
17 }
18}

Middleware - systemy obronne

Middleware działają przed kontrolerami - jak systemy obronne sprawdzające każdego, kto wchodzi do Imperium:

1@Injectable()
2export class ImperiumMiddleware implements NestMiddleware {
3 use(req: Request, res: Response, next: NextFunction) {
4 // Sprawdź czy żądanie przychodzi od obywatela Imperium
5 const userAgent = req.headers['user-agent'];
6
7 if (!userAgent?.includes('RomanImperium')) {
8 console.log('⚠️ Suspicious activity detected!');
9 }
10
11 // Dodaj timestamp do każdego żądania
12 req['timestamp'] = new Date().toISOString();
13 next();
14 }
15}

DTOs i Validation - kontrola rejestrów

DTOs (Data Transfer Objects) definiują strukturę danych - jak rejestry imperialne:

1import { IsString, IsNumber, IsOptional, Min, Max } from 'class-validator';
2
3export class CreateLegionaryDto {
4 @IsString()
5 name: string;
6
7 @IsString()
8 specialization: string;
9
10 @IsNumber()
11 @Min(1)
12 @Max(10)
13 experienceLevel: number;
14
15 @IsOptional()
16 @IsString()
17 backstory?: string;
18}
19
20// Użycie w kontrolerze
21@Post('legion')
22addLegionary(@Body() newLegionary: CreateLegionaryDto) {
23 return this.legionService.recruitLegionary(newLegionary);
24}

Przykład pełnej architektury

1// imperium.module.ts - główny moduł
2@Module({
3 imports: [
4 TypeOrmModule.forRoot(databaseConfig),
5 LegionModule,
6 TributesModule,
7 CampaignModule,
8 ],
9 controllers: [AppController],
10 providers: [
11 AppService,
12 {
13 provide: APP_GUARD,
14 useClass: AuthGuard,
15 },
16 {
17 provide: APP_INTERCEPTOR,
18 useClass: LoggingInterceptor,
19 },
20 ],
21})
22export class ImperiumModule {
23 configure(consumer: MiddlewareConsumer) {
24 consumer
25 .apply(ImperiumMiddleware)
26 .forRoutes('*');
27 }
28}

Najlepsze praktyki architektury

  1. Jedna odpowiedzialność - każdy serwis ma jedno zadanie
  2. Luźne powiązania - moduły są niezależne
  3. Dependency Injection - wszystko przez konstruktor
  4. Walidacja na wejściu - DTOs z dekoratorami
  5. Separacja warstw - kontrolery, serwisy, repozytoria
  6. Obsługa błędów - exception filters
  7. Dokumentacja - Swagger dla API

Architektura NestJS to jak dobrze zaprojektowane Imperium Romanum - każda struktura ma swoje miejsce i przeznaczenie, wszystko współpracuje harmonijnie, a całość jest gotowa na największe kampanie w prowincjach kodu!

W następnym module poznamy szczegóły implementacji kontrolerów - naszych wiernych centurionów!

Ir a CodeWorlds