Pierwszy projekt zwykle mieści się w jednym pliku. Drugi ma ich pięć. Przy dwudziestym nikt już nie wie, gdzie leży obliczanie żołdu - w kontrolerze, w jakimś pomocniku, czy może w trzech miejscach naraz, każde nieco inaczej.
Rzym nie rozrósł się przez dosypywanie ludzi do jednej kohorty. Rozrósł się, bo każdy miał jedną rolę i wszyscy wiedzieli, która to: posłaniec nosi rozkazy, kwatermistrz liczy zapasy, pisarz prowadzi rejestr. NestJS narzuca ten sam podział - i właśnie ta narzucona struktura sprawia, że dwudziesty plik nadal da się znaleźć.
Podstawą architektury NestJS jest Model-View-Controller (MVC) - wzorzec dzielący aplikację na trzy odpowiedzialności: dane, ich prezentację i obsługę żądań.
Nie myl go z wzorcami, które rozwiązują zupełnie inne problemy: Singleton pilnuje, by istniała jedna instancja klasy, Observer rozsyła powiadomienia o zdarzeniach, Strategy pozwala wymieniać algorytm w trakcie działania. Wszystkie trzy spotkasz wewnątrz NestJS, ale to MVC wyznacza kształt całej aplikacji.
W backendzie warstwa View zwykle znika - odpowiedzią jest JSON, nie strona HTML - więc na co dzień pracujesz z dwiema pozostałymi i z modułem, który je spina.
Cała aplikacja NestJS składa się z trzech powtarzalnych elementów. Warto je zapamiętać przez pytanie, na które każdy odpowiada:
Zacznijmy od środka, bo to serwis niesie właściwą wartość:
1@Injectable()
2export class LegionService {
3 private legions = [
4 { id: 1, name: 'Legio X Equestris' },
5 { id: 2, name: 'Legio III Gallica' },
6 ];
7
8 findAll() {
9 return this.legions;
10 }
11}Kolejność deklaracji jest stała:
, potem @Injectable()
, dalej nazwa klasy, na końcu ciało z metodami.export class
I tu pada odpowiedź na pytanie, które w praktyce rozstrzyga o jakości całego projektu: logika biznesowa należy do serwisów - tego, co NestJS nazywa providerami. Nie do kontrolerów, nie do middleware, nie do plików konfiguracyjnych.
Powód jest prosty. Logika w kontrolerze jest przywiązana do HTTP - nie wywołasz jej z zadania wsadowego ani z testu jednostkowego bez udawania żądania. W middleware jest jeszcze gorzej, bo middleware pracuje na surowym żądaniu, zanim wiadomo, dokąd ono trafi. Logika w serwisie jest zwykłą metodą zwykłej klasy - wywołasz ją skąd chcesz.
Kontroler bierze serwis przez konstruktor i deleguje mu pracę:
1@Controller('legions')
2export class LegionController {
3 constructor(private readonly legionService: LegionService) {}
4
5 @Get()
6 findAll() {
7 return this.legionService.findAll();
8 }
9}Zwróć uwagę na proporcje. Metoda kontrolera ma jedną linię - i tak ma być. Cała jej rola to przyjąć żądanie i wskazać wykonawcę.
Kontroler nie tworzy serwisu przez
new. Wpisuje go w konstruktor, a NestJS podaje gotowy - to wstrzykiwanie zależności, mechanizm, który poznasz dokładniej w kolejnych lekcjach. Na razie wystarczy widzieć skutek: kontroler mówi, czego potrzebuje, i nie martwi się, skąd to wziąć.Trzeci element spina dwa poprzednie:
1@Module({
2 controllers: [LegionController],
3 providers: [LegionService],
4})
5export class LegionModule {}Bez tego zgłoszenia NestJS nie wie, że te klasy istnieją - pliki same z siebie nic nie znaczą.
Zauważ, że w module widać całą prowincję naraz: kto przyjmuje żądania i kto wykonuje pracę. Otwierając nieznany projekt, moduły czyta się pierwsze, bo to one pokazują mapę.
Złóżmy to w jeden przebieg. Żądanie GET /legions wędruje tak: trafia do kontrolera
LegionController, ten wywołuje metodę serwisu LegionService, serwis zwraca dane, kontroler oddaje je jako odpowiedź. Moduł nie bierze udziału w locie - zadziałał wcześniej, przy starcie, gdy NestJS budował z niego mapę zależności.Ten schemat powtarza się w całej aplikacji, niezależnie od jej wielkości. Dwudziesty zasób wygląda tak samo jak pierwszy - i dlatego wiadomo, gdzie szukać obliczania żołdu, @name: w serwisie, zawsze w serwisie.
Imperium ma administrację, każdy zna swoją rolę:
@Injectable(), export class, nazwa, ciało z metodami,new,W następnej lekcji przyjrzymy się modułom dokładniej - zobaczysz, jak dzielą aplikację na prowincje i co decyduje o tym, że jedna widzi drugą. A na razie zapamiętaj: kontroler przyjmuje, serwis wykonuje, moduł rejestruje - i ten sam układ powtarza się w każdym zakątku aplikacji.