A vállalati alkalmazások megtervezése és fejlesztése összetett folyamat, amely számos tényező alapos megértését és alkalmazását igényli. Egy technikai vezető vagy architekt számára az elsődleges feladat a vállalkozás üzleti céljainak és hatásainak mélyreható megértése, valamint a siker mérőszámainak világos meghatározása. Az „elasztikus vezetés” szemlélete, amely rugalmasan alkalmazkodik a változó körülményekhez, kulcsfontosságú eleme egy projekt sikerének. Az agilis fejlesztési módszertanok, különösen az agilis mérnöki gyakorlatok, támogatják az alkalmazásfejlesztés hatékonyságát és minőségét, miközben elősegítik a mérnöki kiválóságot és a szakmai igényességet.

Az Angular keretrendszer kiemelt szerepet játszik az enterprise környezetben, különösen sokszínű kódolási paradigma támogatásával és aktív közösségi háttérrel. Az alkalmazások teljesítményének növelése érdekében különböző eszközöket és technikákat alkalmazhatunk, például a szerveroldali renderelést, az alkalmazáshéjat (app shell), a service workereket, valamint fejlett könyvtárakat, mint az RxAngular vagy az Angular Signals. A build folyamatok optimalizálásához olyan eszközök használata ajánlott, mint az Nx vagy az esbuild, továbbá a tesztautomatizálás bevezetése nélkülözhetetlen a folyamatos minőségbiztosítás érdekében.

A tervezési folyamat során kiemelt jelentőségű a Kanban és GitHub projektek alkalmazása, amelyek segítségével hatékonyan szervezhető a feladatok prioritása és nyomon követhető a fejlesztési ütemterv. Az úgynevezett 80-20-as szabály érvényesítése – azaz a legfontosabb funkciókra koncentrálva a fejlesztés során – segít fókuszálni a lényeges elemekre, és csökkenti a komplexitást.

Az üzleti alkalmazások tervezése során a router-first architektúra alkalmazása lehetővé teszi a moduláris és könnyen karbantartható struktúra kialakítását. A funkciómodulok jól elkülönített egységei, a lazy loadinggal kombinálva, hatékonyan csökkentik az induló betöltési időt és optimalizálják az erőforrások kihasználását. A walking skeleton megvalósítása, amely egy minimális, de működőképes alapot biztosít az alkalmazás számára, segíti a folyamatos integrációt és korai tesztelést. A stateless, adatvezérelt tervezés, valamint a komponensek közötti laza kapcsolatok fenntartása elősegítik a skálázhatóságot és a kód újrafelhasználhatóságát. Az Angular és a TypeScript nyújtotta lehetőségek maximális kihasználása, például az ES modulrendszer használata, jelentősen növeli a fejlesztés hatékonyságát és minőségét.

Az autentikáció és jogosultságkezelés tervezése kritikus eleme a vállalati alkalmazásoknak. A JWT (JSON Web Token) életciklusának ismerete, a típusbiztonság és biztonságos adatkezelési operátorok (például optional chaining vagy nullish coalescing) alkalmazása elengedhetetlen a robusztus és megbízható megoldásokhoz. Az absztrakt szolgáltatások és az objektumorientált programozási elvek segítségével újrahasznosítható és könnyen bővíthető autentikációs modulokat hozhatunk létre. A cache-elés és a memória alapú hitelesítési szolgáltatások alkalmazása javítja a felhasználói élményt és csökkenti a szerver terhelését.

Fontos megérteni, hogy az alkalmazás fejlesztése során nem csupán technikai döntésekről van szó, hanem a felhasználói élmény, a fenntarthatóság és az üzleti érték közötti egyensúly megtalálásáról. A rugalmasság, az alkalmazkodóképesség és a folyamatos tanulás, valamint az agilis szemlélet támogatja a hosszú távú sikert. A tervezésnek ezért nemcsak a jelenlegi követelmények kielégítésére kell irányulnia, hanem a jövőbeni bővítések és változtatások lehetőségeinek megőrzésére is.

Hogyan biztosíthatjuk a hitelesítést REST és GraphQL API-k esetén?

A RESTful és GraphQL API-k hitelesítése alapvetően elválik egymástól, de mindkettő esetében fontos szerepet kapnak az olyan mechanizmusok, mint a token alapú hitelesítés, jogosultságkezelés és a felhasználói adatok védelme. Az alábbiakban a hitelesítési és engedélyezési rendszerek megvalósítását vizsgáljuk meg, amelyeket különböző keretrendszerekben – például Express.js-ben és Apollo-ban – alkalmazhatunk.

Az Express.js egy népszerű web framework, amely egyszerű és könnyen használható middleware-eket kínál a különböző funkciók integrálásához. Az egyik alapvető middleware, amelyet gyakran alkalmazunk a hitelesítéshez, az authenticate függvény. Ez a middleware a kérések fejléceiben található autentikációs token alapján ellenőrzi a felhasználó érvényességét. Ha a token érvényes, a felhasználói adatokat hozzáfűzi a válaszhoz, így biztosítva, hogy a szerver könnyen hozzáférhessen a hitelesített felhasználói információkhoz. Ha a hitelesítés nem sikeres, akkor a rendszer válaszként egy 401-es hibát küld, amely jelzi, hogy a felhasználó nincs hitelesítve.

Amikor a REST API-t használjuk, például a /me végponton, a middleware közvetlenül hozzáférést biztosít a felhasználó adataihoz, miután sikeresen hitelesítették. A rendszer könnyedén integrálható más Express.js funkciókkal, és a felhasználói jogosultságok kezelésére is alkalmas. Például, ha az adott művelethez szükséges egy specifikus szerepkör, a middleware ellenőrzi azt, és ha a felhasználó szerepköre nem egyezik, akkor a rendszer visszautasítja a kérést.

A GraphQL esetében a helyzet más, mivel a hitelesítés és engedélyezés különböző szinteken valósul meg. A GraphQL API-ban a hitelesítési logika gyakran egy központi middleware-en keresztül történik, amelyet a szerver beállít a /graphql útvonalhoz. Az autentikációs folyamat itt is hasonló, de figyelembe kell venni, hogy egyes műveletek – mint például az introspekció és a bejelentkezés – nem igényelnek hitelesítést. Ezt a viselkedést az authOverridingOperations paraméter segítségével lehet beállítani, amely lehetővé teszi az ilyen műveletek szabad hozzáférését a felhasználók számára, miközben az összes többi kérés hitelesítésére szükség van.

A GraphQL autentikációs folyamata más megközelítést igényel, mivel itt a válaszokat a kérés kontextusában tároljuk, hasonlóan ahhoz, ahogyan az Express.js-ben a res.locals működik. A hitelesítés után a kérés kontextusába beépíthetjük a felhasználói adatokat, és a authorize metódus segítségével a válasz feldolgozása előtt ellenőrizhetjük, hogy a felhasználó jogosult-e a kért erőforrás elérésére.

A REST és a GraphQL autentikációját követően a következő lépés a testreszabott hitelesítési szolgáltatók megvalósítása, amelyek biztosítják, hogy a front-end alkalmazások megfelelő módon kommunikáljanak a backend-del. A következő példákban bemutatjuk, hogyan építhetjük fel a REST és a GraphQL autentikációt a front-end Angular alkalmazásban.

A REST autentikációs szolgáltató egy egyszerű HttpClient segítségével valósítható meg. A login folyamat során a felhasználó e-mail címét és jelszavát egy HTTP POST kérésben küldjük el a szerverre, amely válaszul egy JWT tokent ad vissza. Ezt a tokent ezután minden további kérésnél használjuk a felhasználó azonosításához. A getCurrentUser metódus segítségével pedig lekérhetjük a hitelesített felhasználó adatokat, amelyek a backend szerverről érkeznek.

A GraphQL autentikációs szolgáltató az Apollo Client használatával építhető fel. Az authProvider metódus itt is a felhasználó bejelentkezését kezeli, és a válaszban kapott JWT tokent használja a további kommunikációhoz. A transformJwtToken metódus biztosítja, hogy a tokenből kinyert felhasználói adatokat megfelelő formátumban kezeljük, míg a getCurrentUser lekérdezi az aktuális felhasználót a GraphQL szerverről.

A két megközelítés közötti különbségek elsősorban abban rejlenek, hogy míg a REST API esetében a HTTP kérésre alapozott hitelesítés zajlik, addig a GraphQL-ban a lekérdezések és mutációk révén történik az adatok elérése, az autentikációs logika pedig egy központi helyen történik.

Fontos megjegyezni, hogy a RESTful API-k esetén a HTTPS használata elengedhetetlen, mivel így biztosítható, hogy a felhasználói adatokat titkosítva továbbítsuk az interneten. A nyílt hálózaton történő adás-vétel során az adatbiztonság a legfontosabb szempont, különösen akkor, ha a felhasználók érzékeny információkat, mint például jelszavakat vagy személyes adatokat adnak meg.

A teljes rendszer megfelelő működése érdekében mind a backend, mind a frontend szintjén biztosítani kell, hogy a hitelesítés során alkalmazott tokenek és jogosultságok a megfelelő védelmet élvezzék, és hogy a felhasználók csak azokat az erőforrásokat érhessék el, amelyekhez jogosultságuk van.

Hogyan működik a master/detail nézet irányítása Angularban, és mit jelent a felhasználóbarát adatbetöltés?

Az Angular router egyik kevésbé intuitív, ám rendkívül fontos aspektusa a named outlets, különösen a master/detail nézetekben történő alkalmazásuk során. Bár a keretrendszer belső működése logikus mintázatokat követ, a fejlesztők számára ezek gyakran rejtve maradnak, és csak tapasztalati úton válnak érthetővé. Vegyük például azt az esetet, amikor a master outlet explicit módon üres útvonallal van definiálva: master: ['']. Ez működik. Azonban ha ugyanezt próbáljuk másként, például master: ['someValue'], a router nem tudja helyesen értelmezni az útvonalat, és csendben elhibázza a betöltést – semmilyen hibaüzenet nem figyelmeztet a problémára. Ez a viselkedés abból fakad, ahogyan az Angular az üres útvonalakat értelmezi, és habár ez keretrendszerszinten indokolt, a gyakorlati használat során félrevezető lehet.

A resolve guard használata a ViewUserComponent esetében demonstrálja, milyen előnyökkel jár az adatok előzetes betöltése. Az Angular biztosítja, hogy az adatok már a komponens inicializálása előtt elérhetők legyenek, kiküszöbölve a ngOnInit metódusban való manuális adatkezelést. A Chrome DevTools segítségével jól nyomon követhető, ahogyan a this.currentUser érték automatikusan és pontosan kerül beállításra, ezzel is növelve az alkalmazás teljesítményét és letisztultságát.

A részletes nézet után a master nézet megvalósítása következik egy paginált adattábla formájában. Az UserTableComponent gondoskodik a felhasználói adatok tömeges lekérdezéséről, amelyhez a UserService egy új metódusa, getUsers, kerül bevezetésre. Ez a metódus képes kezelni a pageSize, pagesToSkip, searchText, sortColumn, és sortDirection paramétereket, lehetővé téve a dinamikus adatbetöltést szűréssel és rendezéssel együtt. A lekérdezés során a rendezési irányt a szerver felé pozitív vagy negatív kulcsparaméterrel jelezzük, ahol a mínusz előtag a csökkenő sorrendet reprezentálja.

Az UserTableComponent belső logikája a komponens életciklusához, valamint a MatPaginator és MatSort eseményeihez van kötve. A skipLoading zászló lehetőséget ad a betöltés elkerülésére bizonyos esetekben, míg a refresh$ egy Subject típusú adatfolyam, amely újratöltést kezdeményez. A displayedColumns dinamikusan frissül a demoViewDetailsColumn nevű signal alapján, amely lehetővé teszi egyes oszlopok megjelenítésének ki- vagy bekapcsolását. Az items$ observable egyesíti az összes releváns eseményt – szűrés, lapozás, rendezés –, és ennek eredményeként történik a tényleges adatok lekérdezése és megjelenítése.

A resetPage funkció a felhasználói élmény fontos része: egy új keresés vagy lapozás után visszairányítja a felhasználót az első oldalra, miközben bezárja a részletes nézetet, így elkerülve a kontextus elvesztését. A showDetail metódus a kiválasztott felhasználó részletes nézetének megjelenítését végzi az Angular Router segítségével, a detail nevű outletben. Érdemes megfigyelni, hogy az útvonal generálása relativeTo és skipLocationChange beállításokat is tartalmaz – ezek a paraméterek finomhangolják a navigáció viselkedését anélkül, hogy a böngésző URL-je módosulna.

Fontos megérteni, hogy a UserTableComponent és a hozzá kapcsolódó architektúra valójában egy reaktív adatkezelési minta példája Angularban. Az események streamjei (mint a paginator, sorter, keresési mező) nem szigetszerűen működnek, hanem egyesülnek és összehangoltan frissítik a nézetet. Ez nemcsak a teljesítményt növeli, hanem az adatok konzisztens megjelenítését is biztosítja. A catchError révén kezelhetők a betöltés során fellépő hibák, ezzel a felhasználói visszajelzés és a hibatűrés is professzionális szintre emelkedik.

A fejlesztőnek mindemellett tisztában kell lennie az Angular reaktív programozási modelljének alapelveivel, különösen az Observable, pipe, map, switchMap, tap, debounceTime, és takeUntilDestroyed operátorokkal. Ezek nem csupán API-elemek, hanem egy gondolkodásmódot is képviselnek, ahol az alkalmazás logikája nem lépésről lépésre zajlik

Hogyan alkalmazzuk az NgRx-ot Angular alkalmazásokban?

A router beállítása a lusta betöltődő modulokon és a névvel ellátott kimeneteken hibás lehet, ha nem figyelünk a részletekre. Az ilyen típusú hibák elkerülése érdekében engedélyezhetjük a router hibakereső módját, amelyet a app.config.ts fájl gyökérszolgáltatójának módosításával érhetünk el. Ehhez hozzá kell adnunk a withDebugTracing függvényt, amint az az alábbi példában látható: provideRouter(routes, withDebugTracing()). Ez segíthet abban, hogy könnyebben megtaláljuk a hibákat és nyomon kövessük a router működését.

A Material Design matRipple direktíva lehetővé teszi, hogy egy hullámhatást alkalmazzunk, amikor egy sort kattintanak. Ezt követően megvalósítjuk a kattintási eseménykezelőket, amelyek alapértelmezetten megnyitják a részletező nézetet a showDetail függvény használatával. A felhasználó másik lehetősége, hogy a legjobb oszlopban található "View" gombot kattintva jelenítse meg a részleteket. Végül figyeljük meg a frissítési gomb kattintását, amely frissítést generál a refresh$ Observable-ban. Ez a frissítés az összes többi pipeline-unkban, például a komponensben is feldolgozásra kerül.

Az alábbiakban látható, hogy a táblázat csak a fő nézetet tartalmazza (ellenőrizze, hogy az Angular legfrissebb verzióját használja):

Ezután, ha rákattintunk egy sorra, a ViewUserComponent a részletező kimenetben jelenik meg a showDetails függvény segítségével. Az oszlop kiemelése is megjeleníti a választást. Ha a jobb felső sarokban található "View Details" oszlopot bekapcsoljuk, akkor az adott oszlopban a nézetgombok láthatóvá válnak.

Egy további fontos eleme a fejlesztési folyamatnak a frissítés és a szerkesztés funkciók megvalósítása. Az előző fejezetben bemutattuk a szerkesztési gombot, amely egy ceruza ikonnal van ábrázolva, és a felhasználó adatait frissíthetjük. Ezzel egy újabb felhasználó adatainak frissítése is tesztelhető, a módosított rekordot pedig visszaellenőrizhetjük a táblázatban.

Az alkalmazás tesztelésekor fontos, hogy megfelelő tesztadatokat hozzunk létre. A createComponentMock függvény helyett érdemes konkrét komponens-implementációkat importálni, például a NameInputComponent vagy ViewUserComponent komponenseket. Ez azért fontos, mert a createComponentMock nem elég fejlett ahhoz, hogy megfelelően kezelje az adatokat a gyermekkomponensekben.

Miután sikeresen megvalósítottuk az alkalmazás alapvető funkcionalitását, az Angular alkalmazások optimális architektúrájának kiválasztásához és különböző igényeknek megfelelően újabb eszközöket és könyvtárakat kezdhetünk el használni. Az egyik ilyen eszköz az NgRx, amely az alkalmazások állapotkezelését segíti elő a reaktív programozás elveivel.

Az NgRx könyvtár a reaktív állapotkezelést biztosít az Angular számára, és az RxJS-t használva alapvetően lehetővé teszi az alkalmazások állapotának atomikus és kompozícióra épülő kezelést. Az NgRx egy absztrakciós réteget biztosít az RxJS felett, amely illeszkedik a Flux mintához. Az NgRx három fő összetevőből áll: az áruházból (store), az akciókból (action), az akciók küldésére szolgáló dispatcher-ből és az effektusokból (effect).

Az áruház az a központi hely, ahol az alkalmazás állapota tárolódik. Egy reducer segítségével tároljuk el az állapot változásait, és egy selector segítségével olvassuk vissza az adatokat. Az akciók az alkalmazásban történő eseményeket képviselik, míg az effektusok az akciók hatására zajló aszinkron műveleteket kezelik, például API-hívások kezelését.

A LocalCast Weather alkalmazásban az NgRx-t használjuk a keresési funkciók megvalósításához. A rendszer működését az alábbi diagram szemlélteti: az alkalmazásban az akciók és az állapotok együtt működnek az NgRx store és effect rendszerekkel. A keresési akciók elindítását követően az állapotok frissülnek, és az alkalmazás visszajelzést ad a felhasználónak az új adatok alapján.

A rendszer így az alábbi módon működik:

  1. A CitySearchComponent elindítja a keresési akciót.

  2. Az akciók megjelennek az @ngrx/action adatfolyamon.

  3. A CurrentWeatherEffects reagál a keresési akcióra, és elindítja a keresést.

  4. A WeatherService végrehajtja a keresést és lekéri az aktuális időjárást egy külső API-ból.

Miután az adatokat lekértük, a rendszer az új adatokat egy reducer segítségével tárolja el a store-ban. Az adatok új állapotba kerülnek, amely már tartalmazza a keresési eredményeket. Ezt követően egy Angular komponens segítségével jeleníthetjük meg az információkat a felhasználó számára.

Ha összehasonlítjuk a BehaviorSubject és az NgRx használatát a hasonló funkciók megvalósításához, láthatóak a különbségek az implementálásban. Az NgRx használata előnyös lehet, amikor az alkalmazás állapotát tisztán és hatékonyan akarjuk kezelni, különösen akkor, amikor az aszinkron műveletek kezelése és az állapotváltozások nyomon követése szükséges. A BehaviorSubject egyszerűbb, de nem biztosít olyan erős támogatást az állapotkezeléshez és a mellékhatások kezeléséhez, mint az NgRx.

Fontos megérteni, hogy az NgRx nem csak az állapotkezelést teszi egyszerűbbé, hanem segít a programozási hibák minimalizálásában is azáltal, hogy a mellékhatásokat a megfelelő helyeken izolálja. Az NgRx-ot érdemes alkalmazni olyan nagyobb Angular alkalmazásoknál, ahol a komplex állapotkezelés és a skálázhatóság kulcsfontosságú tényezők.