Ovaj priručnik predstavlja seriju članaka fokusiranih na izgradnju efektivne aplikacijske arhitekture u Angularu, temeljeno na iskustvima korištenim u Ekobitu.
Preduvjeti
Za čitanje ovog članka se pretpostavlja da ste upoznati s osnovama NuGet paketa i paradigme single-page aplikacija (SPA), koja se koristi prilikom izrade web aplikacija na strani klijenta. Pretpostavlja se i da ste upoznati s TypeScript-om i da imate barem neko iskustvo u izradi aplikacija u Angularu.
Proširenje vaše arhitekture
Za većinu manjih aplikacija i arhitektura gdje odvajanje odgovornosti nije relevantno, dovoljno je imati samo aplikaciju u src mapi.
Kako ćemo napredovati u složenosti aplikacije ispunjavajući poslovne zahtjeve, aplikacija će evolvirati stvarajući više odvojenih dijelova (tj. modula) koji će svaki služiti svrsi pružanja svoje specifične funkcionalnosti krajnjem korisniku. Ova paradigma je podržana i dobro poznata ne samo u okviru Angular-a, već i TypeScript-a (a u posljednje vrijeme i JavaScript-a). Uvijek postoji jedan korijenski (root) modul koji može sadržavati i druge module koji se mogu dinamički učitati.
Kasnije bi moglo biti povezano i više projekata, također u istoj aplikacijskoj mapi (i u source code repozitoriju), jer se složenost i potrebe projekata obično samo povećavaju 😉
Modularna arhitektura projekta u Angularu
Za stvaranje novog modula unutar postojeće Angular aplikacije možete koristiti sljedeću naredbu:
ng g module <module-name> –routing=true
Ova naredba će stvoriti novi modul u mapi nazvanoj po nazivu modula, zajedno s dodatnim modulom za routing.
Ono što sve module spaja zajedno je Angular routing, jer svaki put kada se izvrši poziv za dohvaćanje neke komponente ili drugog dijela modula, to se prvo pronalazi i provjerava kroz modul za usmjeravanje (routing).
Kao što se vidi sa snimke zaslona u nastavku, korijenski modul (obično nazvan „app.module“) ima svoj modul usmjeravanja (nazvan „app-routing.module“), koji definira navigacijske rute za pristup njegovom sadržaju. Na svaki zahtjev klijenta, ove rute će se analizirati od vrha do dna kako bi se utvrdilo ispravno podudaranje, a potom će, ako je zahtjev valjan, klijent biti upućen u odgovarajući sadržaj. Kao u donjem primjeru, korijenski modul može definirati rute izvan modula i također evaluirati staze koje se dinamički učitavaju:
Kao što je već spomenuto, svaka ruta bi mogla biti validirana prije navigacije korisnika, a to se obično radi kako bi se zaštitio sadržaj korisničkom autorizacijom (poput OAuth autorizacije ili na neki drugi način).
Ostali moduli imaju vlastitu navigaciju u odgovarajućim modulima za usmjeravanje i oni se inicijaliziraju s naznakom da odgovaraju podređenim („child“) rutama.
Postoje alternativni načini kako spojiti sve module zajedno, poput učitavanja modula pomoću Angular-ovog compilera i korištenja tvornica modula (module factories), ali, barem za uobičajene slučajeve, to nije potrebno.
Dijeljenje podataka između modula
Kako bi se kôd unutar modula učinio dostupnim, modul se može uvesti iz glavnog (root) modula.
U gornjem primjeru, na ovaj način se devices („podređeni“) modul uvozi u korijenski modul. To je izravniji način povezivanja modula zajedno, budući da se modul sastavlja zajedno s glavnim modulom i njegova se veličina u skladu s tim povećava. Koristite ovaj pristup kada postoji čvrsta veza između glavnog i uvezenog modula, jer će tada sve komponente i direktive biti implicitno dostupne (na primjer, u HTML predlošcima).
Najčešći problem takve organizacije aplikacija je taj što se “podređeni” modul ne može uvesti u neki drugi podređeni modul, jer je već uvezen u korijenski modul. Stoga se koristi paradigma zajedničkog ili dijeljenog (shared) modula.
Pretpostavlja se da se zajednički modul sastoji od kôda koji nije čvrsto povezan s ostatkom strukture aplikacije. On obično sadrži direktive, pipe, modele, servise i pomoćne klase koje se koriste kroz cijelu aplikaciju, imajući na umu DRY („Don’t repeat yourself“) princip. Neki dobavljači biblioteka koriste ovu paradigmu za razdvajanje isporučenih aplikacijskih svežnjeva ili bundle-ova (isto tako poznatih kao „barrela“) prema vrsti (komponente, direktive, validatori) ili skupu funkcionalnosti (npr. Dropdown kontrola, Pie Chart kontrola, itd.).
Zajednički modul treba uvesti svaki modul, ali ne i korijenski modul. Koristite ovaj princip da biste znatno povećali ponovnu upotrebu kôda i smanjili veličinu modula. Budite oprezni zbog povećane složenosti aplikacije zbog komunikacije između modula!
Dijeljenje podataka između dinamički učitanih modula nešto je teže jer ne postoji izravna komunikacija između glavnog i učitanog modula.
Postoji nekoliko načina zaobići to – najlakši od njih je putem parametrizacije ruta.
Tijekom navigacije prema udaljenoj (vanjskoj) ruti s parametrom rute, kôd unutar ciljane komponente trebao bi očitati parametar rute s routera i djelovati u skladu s tim.
Alternativno, atributi komponenata (kao u bilo kojem drugom uobičajenom slučaju) mogu se postaviti putem @input varijable ciljne komponente. Međutim, budite oprezni: komunikaciju putem (singleton) servisa treba izbjegavati, jer ne postoji jamstvo o dostupnosti i načinu djelovanja servisa unutar ciljne komponente.
____________________________________________________________
Napomena: Sav izvorni kôd koji se koristi u ovim člancima je dostupan ovdje.