RxJS je knihovna, která umožňuje implementaci kódu v reaktivním paradigmatu. Umožňuje asynchronní manipulaci s datovými toky, transformace, filtrování a řízení datových událostí. Reaktivní programování je evolucí programování na bázi událostí. V tradičním event-driven programování byste definovali obslužnou funkci a připojili ji k nějakému zdroji události. Například, pokud máte tlačítko pro uložení dat, které spouští událost onClick, vytvoříte funkci confirmSave, která se aktivuje při každém kliknutí a zobrazí potvrzovací dialog „Jste si jistí?“. Tento přístup je jednoduchý, ale v případě opakovaných kliknutí uživatele bude každé kliknutí iniciovat nové potvrzení, což není efektivní.

Pub/sub (publish-subscribe) je pokročilý model programování na bázi událostí, který umožňuje připojit více obslužných funkcí k jedné události. Například pokud aplikace přijme nová data, může se tato událost šířit mezi více funkcemi, které s daty pracují. Některé funkce mohou data ukládat, jiné mohou aktualizovat vizuální prvky, jako jsou zprávy pro uživatele. Tento přístup je flexibilní, ale může vést k zbytečným nebo nadbytečným informacím, pokud jsou všechny funkce spuštěny najednou a každý se s daty zachází jinak.

Reaktivní programování, jak jej nabízí RxJS, přináší změnu: všechno je zpracováváno jako datový tok. To znamená, že každá událost je součástí širšího toku dat, který se vyvíjí v čase. Místo toho, abychom reagovali na jednotlivé události, můžeme ovládat a filtrovat tyto toky, což poskytuje mnohem větší kontrolu. Představte si například, že sledujete kliknutí myší. Bez jakéhokoli omezení by každý klik vytvořil nový záznam, což by bylo přetížení. Pokud použijeme funkci throttle, omezíme to na jeden záznam každých 250 milisekund. Tento nový stream můžete následně upravit pomocí funkce map, která souhrnně spočítá počet kliknutí za daný časový úsek, nebo filtrováním, abyste reagovali pouze na dvojité kliknutí. Tato flexibilita je klíčem k efektivnímu zpracování dat v reaktivním programování.

Pokud vezmeme v úvahu framework Angular, který se rovněž zaměřuje na reaktivní přístup, znamená to posun směrem k efektivnější správě aplikací. Angular 16 přinesl signály, nový způsob, jak zpracovávat změny v aplikaci na úrovni granularity, což znamená efektivní aktualizace konkrétních částí uživatelského rozhraní bez přetížení celého stromu komponent. Tato nová funkce zajišťuje, že se aplikace reaguje pouze na změny, které jsou relevantní pro daný komponent, čímž se výrazně zlepšuje výkon aplikace.

Pokud jde o architekturu Angularu, máte k dispozici několik možností, jak uspořádat svou aplikaci. Můžete používat modulární architekturu, kde aplikace je rozdělena do menších modulů, nebo standalone architekturu, která klade důraz na explicitní správu závislostí a poskytování funkcí přímo pomocí provider funkcí. U těchto přístupů je důležité chápat, že čím lépe definujete a optimalizujete závislosti, tím lépe může framework vygenerovat a optimalizovat váš kód pro produkční prostředí. Zatímco standalone architektura poskytuje výhody v oblasti optimalizace a flexibility, může být o něco složitější pro začátečníky, kteří se s ní setkávají poprvé.

Pokud jde o práci s routerem v Angularu, tento nástroj poskytuje mnoho pokročilých funkcí pro navigaci v Single Page Application (SPA), včetně lazy loading, router outlets, a možností procházky mezi komponentami s ohledem na jejich stav. Klíčovou součástí úspěšného využívání routeru je pochopení konceptu „router-first“, kde aplikace začíná svou strukturu od konfigurace tras a poté se dynamicky generují komponenty na základě těchto tras. To umožňuje efektivní správu navigace a zlepšuje uživatelský zážitek.

Ve všech těchto aspektech je základem reaktivního programování schopnost efektivně řídit tok dat a reagovat na změny v reálném čase. Kromě toho by si čtenář měl uvědomit, že při práci s reaktivními toky je důležité zaměřit se na efektivní manipulaci s daty. Množství dat, které jsou zpracovávány v aplikacích, může rychle narůst, a tak je kladeno důraz na optimalizaci výkonu, aby se aplikace chovala rychle a stabilně. Výběr správného nástroje pro správu stavů, jako jsou signály nebo RxJS, může zásadně ovlivnit jakékoliv rozhodnutí o architektuře a provedení aplikace, a to i ve vztahu k udržitelnosti a škálovatelnosti.

Jak fungují monorepozitáře a proč je jejich použití výhodné, ale i náročné

Monorepozitář (monolithic repository) představuje strategii vývoje softwaru, při které je kód z více projektů uložen v jednom jediném repozitáři. Tento přístup přináší několik klíčových výhod, především ve formě sjednoceného verzování, zjednodušeného spravování závislostí a snadného sdílení kódu mezi projekty.

V monorepozitáři mohou vývojáři snadno přecházet mezi projekty v rámci stejného prostředí IDE, což usnadňuje práci, například při sdílení TypeScriptových rozhraní mezi frontendem a backendem. Tento přístup zaručuje, že datové objekty jsou vždy synchronizovány, což je klíčové pro zachování integrity dat. Vývojáři tak mohou efektivně pracovat s kódem z více projektů, přičemž všechny změny lze provádět v jednom commitovém bloku, což vede k jasnějšímu a přehlednějšímu procesu verzování a nasazování.

Další výhodou monorepozitářů je zjednodušení kontrol kvality kódu a standardů. Všechny procesy, jako jsou Pull Requesty (PR), nasazování a testování, probíhají v jednom centrálním místě, což znamená, že není nutné koordinovat změny mezi více repozitáři, nasazování na různých serverech nebo různých systémech. To vše činí údržbu kódu přehlednější a efektivnější.

Nicméně, monorepozitář není univerzální řešení. Ve velkých aplikacích může být problémem příliš mnoho souborů v projektu, což vyžaduje výkonný hardware pro vývojáře a robustní CI/CD servery. Nasazení takového systému se může stát náročným a komplexním úkolem. Tento problém by mohl být ještě více komplikován, pokud do týmu vstoupí nový člen, který by se mohl cítit zahlcen množstvím kódu a komplexnosti projektu.

Monorepozitáře byly známé již od začátku 2000. let, ale až do nedávna byly prakticky nevyužitelné pro většinu vývojářských týmů, s výjimkou největších technologických firem. V roce 2019, když Google uvolnil open-source nástroj Bazel, se tato strategie stala dostupnější pro menší projekty. V oblasti JavaScriptu a TypeScriptu se k tomu připojil i Nx, nástroj vyvinutý bývalými zaměstnanci Googlu. Nx je moderní build systém, který poskytuje plnou podporu pro monorepozitáře a silné integrace. Jeho cloudové možnosti, které umožňují distribuovanou cache a paralelní zpracování, optimalizují sestavování kódu bez nutnosti investovat do složité infrastruktury.

Přestože Nx nabízí rozsáhlé možnosti a integrace pro vývoj velkých týmů a firem, není bez svých nevýhod. Je to sofistikovaný nástroj s strmou křivkou učení, což znamená, že jeho zavedení do týmu může být časově náročné a vyžaduje zkušenosti a odbornost v různých technologiích, jako jsou JavaScript, TypeScript, Git, Nx, Angular a další. Pokud není správně nakonfigurován a udržován, může se snadno stát, že takto komplexní nástroje zatíží vývojáře a zpomalí jejich práci.

Na moderním hardwaru, který není zatížen zbytečnými podnikovými zpomalovači, jsou aplikace Angular schopné dostatečně rychle sestavit několik set komponent. Tento proces se ještě zrychlí s novými nástroji jako esbuild a Vite. Nx má další výhodu v podobě centrálního řízení závislostí a možnosti sdílet knihovny mezi různými projekty. To znamená, že při správném využití lze minimalizovat problémy s konflikty při sloučení kódu. Přesto je důležité pečlivě posoudit potřeby a cíle projektu před rozhodnutím, zda se do monorepozitáře vůbec pustit.

Někteří odborníci považují monorepozitář a nástroje jako Nx za standardní součást moderního vývoje, ale není to vždy nezbytné pro každý projekt. V případě malých týmů nebo projektů, které nevyžadují složitou infrastrukturu a správu, může být použití monorepozitáře přehnané. Stejně tak zvažte náklady na implementaci a údržbu těchto nástrojů, které mohou převýšit přínosy. Je proto klíčové vždy zvážit konkrétní potřeby projektu a týmu a přizpůsobit technologie těmto potřebám.

Jak navrhovat API a využívat interaktivní dokumentaci: REST a GraphQL

Integrace specifikace přímo do vašeho kódu přináší značné výhody, zejména pokud jde o zajištění konzistence mezi tím, co očekává server, a tím, co je skutečně implementováno. Pokud například API přijímá GET požadavek na cestu /me, vývojář ví přesně, jak by měl server reagovat. Pokud uživatel existuje, server vrátí objekt User, pokud ne, vrátí chybu 401 ve formátu UnauthorizedError. Takový přístup výrazně usnadňuje testování a vývoj, protože používání automatizovaných nástrojů umožňuje generování interaktivního uživatelského rozhraní Swagger, které vývojáři a testeři mohou využívat přímo z webového rozhraní.

S rozvojem API implementace je velmi snadné držet specifikaci aktuální. Když je tento proces zjednodušen a usnadněn, celý tým má větší motivaci udržovat Swagger UI v aktuálním stavu, protože to přináší přínosy všem zúčastněným. Tímto způsobem vytváříme tzv. „virtuózní cyklus“, který umožňuje mít živoucí dokumentaci. Když běžně původní návrhy zastarávají a stávají se neaktuálními, zde máme automatizované a interaktivní řešení, které přináší neustálou hodnotu. V tomto případě se budeme zaměřovat na dvě knihovny, které nám pomohou integrovat inline specifikaci přímo do kódu:

  • swagger-jsdoc: Tato knihovna umožňuje implementovat specifikace OpenAPI přímo nad relevantními částmi kódu pomocí identifikátoru @openapi v JSDoc komentářích, čímž generuje soubor swagger.json.

  • swagger-ui-express: Tato knihovna spotřebovává soubor swagger.json a zobrazuje interaktivní Swagger UI na webovém rozhraní.

Při konfiguraci Swaggeru v Express.js je třeba postupovat následovně:

  1. Instalace potřebných závislostí:

    ruby
    $ npm i swagger-jsdoc swagger-ui-express
    $ npm i -D @types/swagger-jsdoc @types/swagger-ui-express
  2. Ukázka souboru docs-config.ts, který konfiguruje základní definici OpenAPI:

    typescript
    import * as swaggerJsdoc from 'swagger-jsdoc';
    import { Options } from 'swagger-jsdoc';
    import * as packageJson from '../package.json'; const options: Options = { swaggerDefinition: { openapi: '3.1.0', components: {}, info: { title: packageJson.name, version: packageJson.version, description: packageJson.description, }, servers: [
    { url: 'http://localhost:3000', description: 'Local environment' },
    {
    url: 'https://mystagingserver.com', description: 'Staging environment' },
    { url: 'https://myprodserver.com', description: 'Production environment' },
    ], },
    apis: ['**/models/*.js'], }; export default app;

V rámci tohoto procesu je nutné ručně zajistit, aby specifikace a kód byly synchronizovány. Tento proces lze plně automatizovat, včetně generování TypeScriptových API handlerů, což pomáhá předejít chybám v kódování.

Jakmile je jasné, jak můžeme navrhovat REST API a vytvářet k němu živoucí dokumentaci, je čas se podívat na GraphQL, které má tyto principy zabudovány přímo do svého návrhu.

GraphQL API

GraphQL, jazyk dotazů pro API vyvinutý Facebookem, představuje moderní, flexibilní, robustní a efektivní alternativu k tradičním REST API. Na rozdíl od REST, které vystavuje pevně definované endpointy pro každý zdroj, GraphQL umožňuje klientům požadovat pouze ta data, která skutečně potřebují. To znamená, že klienti mohou strukturovat odpovědi podle svých požadavků, což vede k menším problémům s nadměrným nebo nedostatečným načítáním dat.

Důležitost správného návrhu API kolem hlavních datových entit nelze podcenit. V této oblasti GraphQL exceluje. Jeho typový systém zajišťuje, že API je navrženo kolem těchto datových entit a poskytuje jasnou smlouvu mezi týmy frontendu a backendu. Tento typový systém, definovaný ve schématu GraphQL, funguje jako kontrakt, který specifikuje datové typy, jež mohou být načteny, a soubor dostupných operací.

Pro vývojáře frontendu je práce s GraphQL osvěžujícím zážitkem. Introspektivní povaha GraphQL znamená, že schéma lze dotazovat pro podrobnosti o sobě. Tato samo-dokumentující funkce zajišťuje, že vývojáři mají vždy aktuální referenci, což eliminuje potřebu samostatně udržované dokumentace. Tento přístup je zvláště výhodný pro agilní týmy v podnikovém prostředí, kde čekání na dokumentaci nemusí být vždy reálné.

Existují interaktivní prostředí jako GraphQL Playground nebo GraphiQL, kde mohou vývojáři testovat a prozkoumávat GraphQL dotazy v reálném čase. Stejně jako Swagger UI pro OpenAPI, tato rozhraní poskytují okamžitou zpětnou vazbu, což umožňuje vývojářům pochopit strukturu, typy a operace API. Tento praktický přístup zkracuje křivku učení a podporuje hlubší pochopení možností API.

GraphQL schéma

Schéma GraphQL je srdcem každého GraphQL API, jelikož popisuje strukturu a schopnosti API definováním typů a vztahů mezi nimi. Tyto typy modelují hlavní datové entity, se kterými API pracuje.

  1. Definice datových objektů pomocí klíčového slova type:

    graphql
    type User {
    address: Address dateOfBirth: String email: String! id: ID! level: Float name: Name! phones: [Phone] picture: String role: Role! userStatus: Boolean! fullName: String }

Tento typ User má základní pole jako id a email, které reprezentují primitivní datové typy jako ID, String, Int, Float a Boolean. Symbol ! označuje povinná pole. Můžeme také definovat vztahy mezi typy, například mezi Name a Phone. Pole phones je definováno jako pole objektů typu Phone.

  1. Definice výčtových typů (enum):

    graphql
    enum Role {
    None Clerk Cashier Manager }
  2. Použití rezervovaného typu Query pro definování způsobu získávání dat:

    graphql
    type Query {
    user(id: ID!): User
    }
  3. Použití rezervovaného typu Mutation pro definování způsobu modifikace stavu:

    graphql
    type Mutation { login(email: String!, password: String!): JWT
    createUser(userInput: UserInput!): User
    }
  4. Definice vstupních objektů pomocí klíčového slova input:

    graphql
    input UserInput { address: AddressInput dateOfBirth: String email: String! }

Tento přístup nejen zjednodušuje komunikaci mezi frontendem a backendem, ale také zajišťuje flexibilitu a efektivitu při práci s daty, což je v moderních vývojových prostředích klíčové.

Jak se změnila architektura Angularu a co přinesla jeho přestavba v roce 2016

Původní verze Angularu, známá jako AngularJS nebo 1.x, byla v roce 2000 považována za průkopníka éry jednostránkových aplikací (SPA), což byla technika, která „klamala“ prohlížeče do toho, že interaktivní aplikace s více stránkami může běžet na jediném index.html. Tento přístup vedl k popularizaci dvoucestného datového vazby (two-way binding), který automaticky aktualizoval zobrazení, aby odpovídalo stavu aplikace ve ViewModelu. K tomu AngularJS využíval tzv. Change Detection, který sledoval změny v Document Object Modelu (DOM) a v samotném stavu aplikace. Tato metoda umožňovala prohlížeči a aplikaci udržet krok s uživatelskými interakcemi a reagovat na změny.

Významným omezením tohoto přístupu byla výkonnost aplikace. Jelikož Change Detection běží v rámci složitého vykreslovacího cyklu, připomínajícího například herní motory, mělo to vliv na počet snímků za sekundu (FPS), což se projevovalo ve „trhaném“ uživatelském zážitku. S rostoucí složitostí webových aplikací se ukázalo, že stará architektura AngularJS nebude stačit pro udržení vysokého FPS, což vedlo k nutnosti přepsat celý framework.

Přepis v roce 2016, který přinesl novou verzi známou jednoduše jako Angular, měl za cíl řešit dvě klíčové oblasti: výkon a vývojářskou zkušenost. Před nástupem populárních knihoven a frameworků jako Angular, React a Vue trpěl webový vývoj neřízenou složitostí, což vedlo k neustálým změnám frameworků a knihoven, které se měnily každý týden. Tyto frameworky přinesly sliby o univerzálně opakovaně použitelných komponentách a usnadnění učení, vývoje a škálování aplikací. V mnoha případech to fungovalo, ale s rostoucími nároky na složitost webových aplikací se problémy, které trápily první jednostránkové aplikace, opět začaly objevovat.

Angulární přestavba tedy byla nezbytná. V rámci této přestavby se důraz kladl nejen na výkon aplikací (významně se zlepšila optimalizace pro nízký počet snímků za sekundu), ale také na zajištění skvélé vývojářské zkušenosti (DevEx), která se stala nezbytnou pro zdraví vývojářů a udržitelnost projektů.

Základy moderního webového vývoje se od svého vzniku dramaticky změnily. S rostoucími nároky na složitost aplikací byla nutná nová architektura, která by umožnila vývojářům udržet krok se stále se zvyšujícími nároky na interaktivitu a komplexnost webových aplikací. Frameworky jako Angular, React a Vue se postupně etablovaly, ale nebyly jedinou odpovědí na všechny problémy. Historie vývoje webových aplikací ukázala, že změny jsou nevyhnutelné, ale stejně tak je důležité zaměřit se na spokojenost vývojářů, která může být rozhodující pro úspěch celých firem.

Pokud se podíváme na širší kontext vývoje webových aplikací, musíme pochopit, proč jsme vůbec začali používat frameworky jako Angular, React nebo Vue. Různé iterace webu ukazují, že v některých případech může framework přestat být nezbytný a je třeba ho opustit, v jiných situacích je však stále klíčový pro úspěch a stabilitu aplikace. Vývoj webových aplikací je tedy neustálým vyvažováním mezi výhodami a nevýhodami různých nástrojů.

Nezapomínejme, že historie webových frameworků nás naučila několik základních pravd: změna je nevyhnutelná, a že štěstí vývojářů je křehké, ale zásadní pro úspěch jakéhokoli projektu. Pokračování v implementaci nových funkcí nebo frameworků bez správného pochopení toho, co způsobuje problémy ve výkonu a spokojenosti vývojářů, může vést k neúspěchům i u nejlepších technologií.

Jak efektivně využívat Router a pomocné komponenty pro zobrazení dat v Angular aplikacích

V Angular aplikacích je jedním z nejefektivnějších způsobů organizace uživatelského rozhraní pomocí komponent a směrování (routing). V tomto procesu lze využít různé techniky pro správu dat, jako je například použití resolverů nebo asynchronní načítání dat. V této kapitole se podíváme na několik klíčových aspektů implementace a refaktoringu komponent v rámci aplikace, která používá směrování pro zobrazení uživatelských profilů a jejich správy.

Při práci s komponentou, která zobrazuje data o uživatelském profilu, máme několik možností, jak tato data načítat. V případě, že uživatel je již v rámci aplikace přihlášen a profil je k dispozici ve směrovacím snapshotu, můžeme použít jednoduchý způsob načítání těchto dat. Pokud však profil není k dispozici, aplikujeme logiku pro načítání dat z cache nebo pomocí autorizačního servisu, který vrátí aktuálního uživatele. Tento přístup je efektivní, protože minimalizuje nutnost opakovaného načítání dat, pokud uživatel provádí akce v rámci jedné relace.

Příklad implementace ukazuje, jak můžeme načítat a zpracovávat data uživatele za pomoci několika asynchronních operací. V tomto případě používáme metodu combineLatest, která sloučí hodnoty z několika zdrojů dat (např. cache a aktuální uživatel), a následně je předáme do metody patchUser. Tato metoda nejen že aktualizuje data ve formuláři, ale také nastaví hodnoty pro identifikátor uživatele nebo datum poslední aktualizace.

Pokud se aplikace nachází ve víceinstančním prostředí, kde stejná komponenta může být použita na různých místech s různými daty, je nutné ji přizpůsobit tak, aby byla schopná reagovat na změny směrování. To znamená, že komponenta musí být schopná se znovu na základě změn v URL nebo dalších parametrech znovu naformátovat a aktualizovat zobrazená data. Tento přístup je realizován pomocí sledování událostí směrovače, což zajišťuje, že při každé změně (např. při změně ID uživatele) bude komponenta znovu načítat potřebná data a vykreslovat nové informace.

Komponenta ViewUser, která se používá pro zobrazení detailů uživatele, je příkladem dobře navržené komponenty, která může být použita v různých kontextech. V první variantě je použita v rámci vícekrokového formuláře pro zobrazení uživatelských informací, v druhé variantě je součástí obrazovky pro správu uživatelů. Aby byla komponenta flexibilní a znovupoužitelná, je nutné, aby mohla reagovat na změny dat, které jsou předávány buď prostřednictvím směrovacího snapshotu, nebo pomocí událostí směrovače. Příkladem takového řešení je zavedení metody assignUserFromRoute, která zajišťuje, že při každé změně URL budou zobrazená data automaticky aktualizována.

Vytváření komponent, které mohou být znovu použity v různých kontextech, je základní dovedností pro vývojáře pracující s Angular frameworkem. Takové komponenty by měly být navrženy tak, aby byly co nejvíce modulární a nezávislé na konkrétních aspektech aplikace. Jedním z příkladů je komponenta ViewUser, která může být použita jak pro zobrazení uživatelských profilů v rámci administrativního panelu, tak pro zobrazení těchto dat ve formě detailního zobrazení v různých sekcích aplikace.

Další silnou stránkou Angular aplikací je využívání pomocných cest (auxiliary routes) pro dynamické zobrazení detailních informací. Pomocí těchto cest lze definovat více oblastí pro zobrazení různých komponent na stejné stránce. Příklad implementace takového řešení ukazuje, jak je možné pomocí routeru definovat hlavní a detailní zobrazení. Tento přístup nejen zlepšuje přehlednost aplikace, ale také umožňuje lepší uživatelský zážitek, kdy uživatel může přepínat mezi různými zobrazeními bez nutnosti opustit aktuální stránku.

Když implementujeme tuto funkcionalitu, můžeme vytvořit komponenty, které se budou vykreslovat do různých výstupních míst definovaných v šabloně, jako jsou outlets. Pro navigaci mezi těmito komponentami použijeme směrovací odkazy (routerLink), které umožňují definovat, jaké komponenty budou zobrazeny v různých oblastech aplikace, aniž by došlo k úplnému přetížení historie prohlížeče. Tento přístup se osvědčuje při vytváření dynamických a interaktivních rozhraní, která umožňují uživatelům snadno přepínat mezi různými zobrazeními.

Aby byla implementace co nejefektivnější, měli bychom také zajistit, že všechny související komponenty jsou dostatečně izolovány a modulární, což znamená, že mohou být snadno přeneseny mezi různými částmi aplikace nebo opětovně použity v jiných modulech.

Při navrhování aplikací s rozsáhlými datovými strukturami, jako je například správa uživatelů, je důležité vždy dbát na optimalizaci načítání dat a správu stavu aplikace. Použití resolverů pro předběžné načítání dat a správné zacházení s asynchronními operacemi výrazně zjednodušuje životní cyklus komponent a zvyšuje výkon aplikace.