V návrhu moderních aplikací je kladeno velké důraz na efektivitu a udržitelnost kódu. To znamená, že bychom se měli vyvarovat složitosti, která není nezbytná, a zaměřit se na to, co je skutečně důležité pro naši aplikaci. V tomto ohledu je klíčové dobře navrhnout komponenty a jejich interakce, což je proces, který vyžaduje dobré porozumění základním principům reaktivního programování, zejména pomocí RxJS, a také zásadám návrhu komponent v Angularu.

Ve většině případů nemusí být pro aplikace nutné využívat složité vzory řízení toku dat, jaké nabízí například NgRx. Tento nástroj využívá mnoho technologií, které se již nacházejí v RxJS, což znamená, že můžeme využít reaktivní přístup k návrhu jednoduchého, beztavového a datově řízeného vzoru pro naši aplikaci. Klíčem je identifikovat hlavní datové entity, se kterými uživatelé budou pracovat, jako jsou faktury nebo osoby, a na těchto entitách postavit celý design. Tento přístup nám pomůže vyhnout se nadměrnému inženýrství a zjednoduší návrh API.

Když definujeme pozorovatelné datové kotvy, můžeme zajistit, že data napříč různými komponentami budou vždy synchronizována. Implementace neměnných datových toků, která se spoléhá na funkcionální reaktivní kód a vyhýbání se ukládání stavu v komponentách, je klíčem k dosažení tohoto cíle. Tento přístup umožňuje vytvořit komponenty, které jsou od sebe oddělené, což zjednodušuje údržbu a rozšiřování aplikace v budoucnosti.

Vytváření komponent s odděleným stavy má zásadní význam pro udržitelnost kódu. Decoupling komponent znamená, že každý modul aplikace může být spravován a testován nezávisle na ostatních. Toho lze dosáhnout využitím Angular funkcí, jako jsou @Input a @Output bindings, a řízením směrování pomocí routeru. Tímto způsobem lze vytvořit komponenty, které jsou jednoduché, snadno se čtou a jsou připraveny pro opětovné použití. Zároveň se tím zajišťuje, že každý komponent je odpovědný za načítání vlastních dat, čímž se eliminuje potřeba nadměrného používání routeru pro správu stavu mezi komponentami.

Další důležitý aspekt při návrhu aplikací je rozlišování mezi uživatelskými ovládacími prvky (user controls) a komponentami. Užití pokročilých a specifických Angular API, jako jsou NgZone nebo Renderer2, je často spojeno s tvorbou vysoce interaktivních a dynamických ovládacích prvků, které mohou být složité na údržbu a přehlednost. Na druhou stranu komponenty představují spíše součásti s jednoduššími funkcemi, jako jsou formuláře s poli, které kapsulují obchodní logiku a jejichž kód je dobře čitelný a udržovatelný.

Když rozlišujeme mezi uživatelskými ovládacími prvky a komponentami, dáváme si tím větší svobodu při rozhodování o tom, jaký kód bude opětovně použitelný a jaký typ komponent bude mít smysl v konkrétním kontextu. K vytváření opětovně použitelného kódu je důležité přistupovat opatrně, protože špatný návrh může vést k plýtvání časem a zdroji. Rámování aplikace s ohledem na opětovné použití prvků pomáhá ušetřit čas v dlouhodobém horizontu, protože správně navržené komponenty lze snadno znovu použít v různých kontextech.

Zároveň je třeba nezapomenout na význam jazykových funkcí, jako je TypeScript a ES, které výrazně přispívají k efektivitě a čitelnosti kódu. Zásada DRY (Don't Repeat Yourself) je v tomto ohledu zásadní. Duplicitní kód je nejen těžko udržovatelný, ale také náchylný k chybám. Je třeba se soustředit na psaní čistého, opětovně použitelného kódu, který je dobře strukturovaný a minimalizuje potřebu opakování. Využití objektově orientovaného návrhu a rozhraní v TypeScriptu může pomoci při vytváření efektivního a udržitelného kódu, který je snadno pochopitelný i pro ostatní vývojáře.

Další výhodou TypeScriptu je možnost definovat rozhraní pro objekty, které nejen dokumentují strukturu dat, ale také pomáhají s transformací dat mezi různými formáty. To usnadňuje práci s daty a umožňuje jejich flexibilní a bezpečné zpracování. Důležitým nástrojem jsou také enumerace, které pomáhají při definování konstantních hodnot, čímž se zvyšuje stabilita a čitelnost kódu, protože snižují riziko chyb při psaní podmínkových výrazů.

Při návrhu aplikací v Angularu bychom se měli vyhnout přehnanému používání dynamických přístupů, pokud to není nezbytné. Udržování jednoduchosti a předvídatelnosti kódu nejen zjednodušuje vývoj, ale také umožňuje aplikaci efektivně růst a přizpůsobovat se novým požadavkům.

Jak navrhnout efektivní navigaci a rozhraní pro aplikaci s více moduly

V tomto článku se zaměříme na tvorbu aplikace s více moduly, která využívá router pro efektivní navigaci mezi různými sekcemi. Důležitým prvkem bude i vytváření navigačních lišt a použití principu "lazy loading" pro zajištění optimálního výkonu aplikace. Ukážeme si, jak implementovat různé navigační prvky a jak rozdělit aplikaci na modulární části, které budou umožňovat snadnou údržbu a rozšiřování aplikace.

V první fázi bylo implementováno "lazy loading" pro modul ManagerModule, což znamená, že tento modul bude načítán pouze tehdy, když uživatel skutečně navštíví stránku spojenou s tímto modulem. Tento přístup nejenže šetří paměť a zrychluje inicializaci aplikace, ale také zlepšuje uživatelskou zkušenost tím, že uživatelé nejsou vystaveni dlouhému čekání na načítání stránek, které neplánují navštívit.

Pro vytvoření jednotného a konzistentního uživatelského rozhraní (UX) v rámci celého modulu bylo rozhodnuto, že navigační lišta pro ManagerModule bude zůstávat na stejné pozici bez ohledu na to, na kterou podstránku uživatel přejde. To znamená, že na každé podstránce bude uživatel vidět stejnou lištu s odkazy na sekce jako User Management a Receipt Lookup, což usnadní orientaci v aplikaci a podpoří rychlé přepínání mezi různými částmi.

V implementaci tohoto navigačního prvku je třeba nejprve vytvořit základní komponentu pro správu uživatelského rozhraní v rámci modulu, kde bude umístěna samotná navigační lišta. Tento prvek musí být schopen sledovat aktivní odkazy, tedy zvýraznit ty, na které uživatel aktuálně klikl, aby bylo jasné, na jaké stránce se nachází.

Pro vytvoření a správné zobrazení podstránek v rámci modulu je nutné nastavit rodičovskou komponentu, která bude zodpovědná za zobrazení jednotlivých podstránek pomocí definování vhodného routování. Pomocí systému rodičovských a dětských rout je možné propojit hlavní komponentu s jejími podřízenými komponentami, jako jsou například komponenty pro správu uživatelů nebo pro kontrolu účtenek.

Pro každý nový modul je nutné se ujistit, že všechny potřebné závislosti jsou správně importovány a že komponenty jsou správně propojeny. Tento přístup nejen usnadňuje údržbu aplikace, ale také zajišťuje, že kód bude snadno rozšiřitelný, pokud se aplikace bude v budoucnu měnit nebo přidávat nové funkce.

U modulu pro správu uživatelů je důležité si uvědomit, že s přihlášením uživatele se otevře možnost pro personalizované zobrazení a pro správu práv. To znamená, že až bude implementována autentifikace a autorizace, aplikace automaticky přizpůsobí dostupné možnosti na základě role uživatele, což poskytne bezpečný přístup k různým funkcím. Tento krok bude zahrnut v pozdější fázi vývoje aplikace, ale je dobré si ho nyní naplánovat pro budoucí integraci.

Jedním z technických aspektů, který by neměl být opomenut, je zajištění správného zobrazení ikonek v navigační liště. Ikony, i když jsou stručné a vizuálně atraktivní, mohou být pro některé uživatele obtížně srozumitelné, proto je vhodné přidat nápovědné texty (tooltips) nebo zlepšit přístupnost pro uživatele s různými potřebami, například přidáním podpory pro čtečky obrazovky. To vše je součástí vytváření aplikace, která je přívětivá pro široké spektrum uživatelů.

Další výzvou při tvorbě navigace je řešení problémů s layoutem. Například, pokud ikony nebo texty v navigační liště nejsou správně rozmístěny, může to vést k vizuální nepřehlednosti. Tato problémy lze řešit různými způsoby, například přidáním mezery mezi ikonami nebo sloučením textových prvků pro zlepšení vzhledu a přehlednosti.

Pro rozšíření a lepší přístupnost aplikace by bylo rozumné přemýšlet o implementaci dalších funkcí, které zlepšují interakci uživatele s aplikací, jako je například podpora pro více jazyků, adaptivní design pro různá zařízení, a případně i přizpůsobení aplikace na základě historických dat o uživatelském chování. Tato vylepšení by měla být zvažována již v raných fázích vývoje, aby se předešlo nutnosti zásadních změn v budoucnosti.

Jak implementovat efektivní autentizaci a autorizaci pomocí JWT a cachingu

Autentizace a autorizace jsou klíčovými aspekty bezpečnosti moderních webových aplikací. V tomto procesu je jedním z nejdůležitějších prvků správná implementace správce přihlášení a odhlášení, který nejen chrání uživatelská data, ale také efektivně spravuje stav přihlášení mezi relacemi uživatele. Tento článek se zaměřuje na implementaci metody přihlášení, odhlášení a cacheování pomocí služby pro uložení JSON Web Tokenu (JWT) v prostředí prohlížeče.

Metoda přihlášení je klíčová pro správný průběh autentizace. Nejprve je třeba zavolat poskytovatele autentizace (authProvider) s e-mailem a heslem uživatele. Po úspěšném přihlášení je třeba dekódovat přijatý JWT, transformovat ho a aktualizovat stav autentizace (authStatus$). Dále se metoda getCurrentUser() spustí pouze v případě, že stav uživatele je autentizován, což znamená, že je přihlášen. Poté se aktualizuje proměnná currentUser$, která obsahuje aktuální informace o přihlášeném uživateli. Jakákoli chyba, která nastane během tohoto procesu, je zachycena pomocí funkce transformError, která obsluhuje chyby a v případě potřeby zajišťuje odhlášení uživatele.

Pokud se objeví chyba během přihlášení, je třeba vykonat odhlášení a zrušit stav autentizace, aby aplikace neukazovala nesprávné informace o uživatelském stavu. V případě chyby se volá metoda logout(), která resetuje stav aplikace. To je zvláště důležité, protože neautorizované pokusy o přístup mohou být detekovány pomocí router guardu, což je technika, kterou podrobněji probereme později v kapitole.

Stejně tak je důležité zavést správnou metodu pro odhlášení uživatele. Pokud uživatel opustí aplikaci nebo se pokusí o neautorizovaný přístup, měla by být prováděna metoda logout(). Tato metoda resetuje stav autentizace tím, že posune výchozí stav autentizace do streamu authStatus$.

Další důležitou částí procesu je implementace cacheování autentizačního stavu. Bez cacheování by se musel uživatel opakovaně přihlašovat při každém obnovení stránky. Pro tento účel jsou k dispozici tři hlavní způsoby uložení dat: cookies, localStorage a sessionStorage. Cookies nejsou ideální pro ukládání bezpečných údajů, protože mohou být snadno ukradeny nebo odcizeny. Navíc mají omezenou kapacitu a mohou být nastaveny na vypršení platnosti. LocalStorage a sessionStorage jsou lepší volbou, protože poskytují větší úložný prostor a jsou izolovány od ostatních webových aplikací. Rozdíl mezi nimi spočívá v tom, že sessionStorage je vymazán při zavření okna prohlížeče, zatímco localStorage přetrvává i po restartu prohlížeče. Vzhledem k těmto vlastnostem je localStorage preferovanou volbou pro dlouhodobé cacheování, protože je bezpečnější a umožňuje uchovávat údaje i po restartu aplikace.

K implementaci cacheování autentizačního stavu v aplikaci můžeme využít službu CacheService, která poskytuje metody pro práci s localStorage. Služba CacheService bude centralizovaným místem pro uložení a načítání autentizačních dat, jako je JWT token. Pomocí této služby můžeme ukládat a načítat hodnoty autentizačního stavu pomocí metod setItem, getItem, removeItem a clear. Při práci s tímto cacheováním je důležité mít na paměti, že bychom nikdy neměli používat cache pro synchronizaci stavu mezi komponentami a službami, aby se předešlo nechtěným vedlejším efektům.

Je důležité si uvědomit, že pro zajištění bezpečnosti bychom neměli uchovávat celý objekt authStatus, ale pouze šifrovaný JWT, který obsahuje dostatek informací pro autentizaci požadavků na server. Tím se minimalizuje riziko úniku citlivých dat. JWT token by měl být šifrován a měl by mít nastavený čas vypršení platnosti, což pomáhá zajistit, že nebude používán dlouho po jeho expiraci. Implementace správného vypršení platnosti a ověření integrity JWT je zásadní pro ochranu před exploitacemi, které mohou být způsobeny zneužitím tokenů.

V praxi můžeme přistupovat k této funkcionalitě prostřednictvím služby AuthService, která bude používat CacheService k ukládání a načítání JWT tokenu. Tato služba bude mít také metodu pro přihlášení uživatele, která zavolá authProvider, dekóduje JWT a následně uloží stav přihlášení do cache.

Důležitým krokem je implementace vzorců pro sledování změn stavu přihlášení pomocí RxJS a BehaviorSubject. Tento přístup nám umožňuje udržet aplikaci bez stavových efektů a současně poskytovat efektivní způsob pro synchronizaci dat mezi různými komponentami. BehaviorSubject nám umožňuje uchovávat aktuální stav autentizace a na základě tohoto stavu dynamicky aktualizovat cache.

Pokud jde o bezpečnost, je vždy nutné věnovat pozornost správnému zacházení s citlivými informacemi, jako jsou JWT tokeny, a zajistit, aby nebyly zneužity prostřednictvím různých útoků, jako jsou XSS nebo CSRF. Tento princip by měl být základem při návrhu autentizačních a autorizačních mechanismů pro všechny aplikace.