Jak zvládáme aktualizace jádra CMS s uživatelským rozhraním přizpůsobeným na míru

Forresti si zakládají na unikátním a výjimečném vzhledu svých webových aplikací. Vše šijeme zákazníkům na míru a z pohledu UX i designu se snažíme být vždy na špičce. Na druhé straně víme, že e-commerce systémy vyžadují neustálý rozvoj, nové funkcionality a aktualizace z důvodu bezpečnosti systému. To znamená, že potřebujeme zajistit průběžné aktualizace jádra a držet všechny naše zákazníky ideálně na poslední stabilní verzi systému Edee.one. Tyto dva základní požadavky jdou proti sobě - čím unikátnější a přizpůsobené řešení vytvoříme, tím pracnější a dražší budou následné aktualizace. Největší komplikace jsou samozřejmě na prezentační vrstvě, protože tam jsou mezi jednotlivými zákazníky největší rozdíly. Co nám tedy umožňuje provádět průběžné povyšování jádra systému relativně levně a rychle?

Komponentový systém

Léta jsme byli přesvědčeni, že komponentový systém je pro prezentační vrstvy efektivním řešením. Celé roky se webový průmysl ubíral spíše směrem MVC frameworků v kombinaci se šablonovacími systémy. Zpracování požadavku vypadá typicky tak, že požadavek zachytí tzv. kontroler, na základě vstupních dat provolá vrstvu modelu (byznys vrstva, databáze), připraví data pro view a následně zavolá šablonu, která data připravená kontrolerem vykreslí.

Tento přístup je pro komponentově orientované systémy nevhodný, protože kontroler musí znát celou strukturu stránky, aby mohl připravit všechna potřebná data. Opětovná použitelnost kontroleru je také velmi nízká, protože je velmi specifický a je těsně svázán s místem, pro které byl vytvořen. Komponenty musí být naopak na svém okolí relativně nezávislé, aby je bylo možné použít na různých místech. S okolím potom spolupracují jasně definovaným kontraktem. Komponenty se mohou do sebe volně vnořovat a mají tyto základní stavební prvky (řada jich je ovšem nepovinných):

  • Java třídu
  • šablonu pro vykreslení
  • datový zdroj
  • události a na ně navázané akce

Stránka je složená výhradně z komponent, stránka pouze požádá své komponenty o vykreslení, každá komponenta následně požádá o vykreslení svých dětí atp. Komponenty následně požádají svůj datový zdroj o data k vykreslení. Datové zdroje jsou reprezentovány nějakým rozhraním, které umožňuje akceptovat řadu různých implementací dle způsobu použití. Datový zdroj slouží pouze pro čtení dat. 

Každá komponenta si udržuje svůj interní stav (pozor, stav nemusí nutně vyžadovat existenci session!). Akce uživatele vyvolávají události na komponentě, které následně “bublají” strukturou komponent až na úroveň stránky. Kdekoliv po cestě může zareagovat tzv. akce, která uživatelskou akci zachytí a zpracuje. Zpracováním akce se může aktualizovat stav komponenty nebo stav systému (data v databázi, souborovém systému atp.). Akce jako jediné mají tzv. side-effecty - tj. mění okolní svět. Všechno ostatní - komponenty, datové zdroje atp. své okolí nijak neovlivňují.

Klíčové je, aby vytvoření nových typů komponent bylo jednoduché jak pro Java vývojáře, tak pro web designery - protože tyto role u nás spolupracují a částečně se i zastupují. Vytvoření nové komponenty tedy nevyžaduje definici nové třídy, ale je možné ji “odvodit” z libovolné existující komponenty (odvozování probíhá na základě tzv. prototypové dědičnosti).

Příklad: Mějme komponentu “select”, který se vykreslí jako rozbalovací menu, základní komponenta definuje Java třídu a šablonu pro vykreslení, také říká, jakého typu musí být dodavatel dat, ale nezná zatím konkrétní implementaci. Web designer vytvoří novou komponentu, která vychází z komponenty “select” a nazve ji “countrySelect”. Této komponentě potom nastaví datový zdroj, který načítá nabídku všech zemí světa. Potom kdekoliv v aplikaci, kde použije komponentu “countrySelect”, zobrazí se mu nabídka zemí v rozbalovacím menu.

Deklarativnost

Důležitým prvkem komponentového systému je deklarativní přístup. Všechny stránky UI jsou definovány jako stromová struktura komponent, kterou lze vizualizovat pomocí našich vývojových nástrojů třeba takto:

vizualizace stromové struktury komponent UI CMS Edee.one

Díky deklarativnímu přístupu se zásadně zvyšuje čitelnost pro vývojáře a je taky jednoznačně dané, jak s danou strukturou systém naloží.

Slovníky komponent

Nejen komponenty ve stránce, ale samotné stránky jsou organizovány ve formě rozsáhlého stromu. Na libovolném uzlu stromu je možné založit tzv. slovník, ve kterém si web designeři připraví sdílené komponenty, které mohou použít na dané stránce či libovolné další “podřízené” stránce (tj. ležící v podřízené struktuře stromu). V globálním slovníku na kořeni stromu jsou definovány základní komponenty (nebo i vzory celých stránek), které jsou sdílené přes celou aplikaci. V podřízených sekcích jsou více specifické komponenty, které se používají pouze v určité části aplikace. Definice komponent ve slovníku je velmi jednoduchá díky principu výše popsané prototypové dědičnosti.

Když si zobrazíme slovník v jednom z našich vývojářských nástrojů, můžeme vidět např. tohle:

slovník komponent UI CMS Edee.one

Slovníky nám zajišťují jednoduchou opakovanou použitelnost komponent (nebo i celých stránek) a díky tomu, že je možné je lehce definovat na libovolném místě stromu, nešpiní se nám tzv. globální kontext a vývojář není zahlcen nabídkou stovek komponent, které v daném místě použití nedávají smysl. Komponenty ve slovnících jsou zároveň dokumentované, takže slouží i jako živá referenční dokumentace.

Příklad: V konkrétním projektu vyžaduje zákazník odlišnou strukturu pro produkty určitého typu - dejme tomu u dřevěných skříněk chce mít konfigurátor, kde si kupující navolí druh dřeva, vzhled dvířek a kování. Web designer nadefinuje nový typ stránky, která vychází z existující stránky “productDetail” (která obsahuje základní strukturu komponent), v jejím těle definuje novou komponentu požadovaného konfigurátoru a umístí ji v existující struktuře stránky na požadované místo. V novém typu stránky potom definuje podmínku jejího zobrazení (tedy pro produkty se štítkem “skříňka”).

Manipulace se strukturou

Díky tomu, že struktura stránek je popsaná tímto strukturovaným způsobem, můžeme si dovolit proti této struktuře provádět změny deklarativní formou. Dejme tomu, že máme komplexní GUI pro správu uživatelů (registrovaných uživatelských účtů), která je zakompilovaná spolu s byznys logikou v jádru systému, který chceme aktualizovat. Pro účely konkrétního projektu potřebujeme u uživatele evidovat kromě základních údajů i jeho “zákaznickou kartu”. Pokud bychom nepoužívali komponentový systém, bylo by to velmi složité. Nám však v tuto chvíli stačí definovat toto jednoduché pravidlo:

V editoru uživatele přidej komponentu text input s id “customerCard” za komponentu s id “email”.

Nebo ještě lépe (tj. odolněji proti změnám původní stránky): 

V editoru uživatele přidej komponentu text input s id “customerCard” jako poslední komponentu ve formuláři s id “userDetails”.

Tak jednoduše, jak čtete výše uvedené věty, tak jednoduše vypadají i pravidla, které vytváří finální UI přizpůsobené aplikace na míru zákazníkovi. Modifikačních pravidel je celá škála a umožňují skutečně silné operace napříč celým systémem.

Jedním pravidlem můžeme například říci - najdi všechny WYSIWYG editory (tj. konkrétní komponentu a všechny z ní vycházející) napříč celou aplikací a doplň tam konkrétní konfiguraci (např. přidej tlačítko pro vložení YouTube videa). Nebo najdi všechny komponenty v konkrétní části stromu a nějakým způsobem je pozměň.

Hezkým příkladem může být třeba naše stránka výpisu kategorie produktů, která má parametrický filtr typicky buď nad výpisem produktů, nebo v levé části pod sekundárním menu. V základu systému máme komponentu nad výpisem produktů, ale pokud se pro konkrétní implementaci hodí mít parametry pod menu, stačí nám k tomu jediná operace “přesuň komponentu XY na místo Z” a tím je požadavek vyřešen.

Díky tomuto mechanismu můžeme mít celé komplexní UI e-shopu připravené v rámci jádra systému a pro konkrétní zákazníky definujeme pouze změny proti tomuto základu. Tj. odstraníme, co není potřeba, přesuneme komponenty na místa, kde jsou požadována, přidáme nové komponenty, které jsou specifická pouze pro tuto konkrétní instalaci. Vývojáři tedy opečovávají strukturovaný DIFF proti sdílenému jádru. Čitelnost DIFFu je pro vývojáře horší, ale proto máme sadu vývojářských nástrojů, které jim umožní si v libovolném okamžiku vizualizovat aktuální strukturu stránky a zorientovat se.

Tento proces je velmi blízký tomu, jak moderní verzovací systémy udržují zdrojový kód. Základem jsou stromové struktury a chytře vymyšlené DIFF a merge algoritmy.

Příklad: V konkrétním projektu si zákazník přeje u každého produktu zobrazovat datum plánovaného naskladnění, pokud produkt není skladem. Webový vývojář vytvoří pravidlo, kterým modifikuje slovníkovou komponentu “productTile”, do které přidá novou komponentu “stockAvailability”, na které definuje podmínku zobrazení “pouze, když produkt není skladem”. Nové komponentě vytvoří šablonu, která zobrazí hodnotu atributu naskladnění z produktu (produkt je proměnná dostupná na všech subkomponentách komponenty “productTile” díky jejímu datovému zdroji). Díky upravení komponenty ve sdíleném slovníku se tato úprava dostane do všech použití komponenty “productTile” v celé aplikaci.

Co se tedy stane při aktualizaci jádra?

Ve chvíli, kdy v systému povýšíme jádro Edee.one, objeví se v aplikaci i nová verze základního uživatelského rozhraní. Díky tomu, že si aplikace udržuje pouze rozdílová pravidla oproti základu, může upgrade proběhnout v ideálním případě bez nutnosti lidského zásahu, a přesto existující zákaznická aplikace dostane všechny opravy základního UI a řadu vylepšení, které mezitím proběhly.

Problémy nastávají pouze v případě, kdy se pravidlo opírá o nějakou strukturu, která byla v jádru změněna - např. komponenta se přidávala do sekce stránky, která byla v jádru odstraněna nebo výrazněji upravena. V takovém případě je vývojář upozorněn na problém s touto modifikací a musí s danou situací nějak naložit.

Tyto problémy však nastávají spíše výjimečně, protože při vývoji jádra se snažíme vyhýbat zpětně nekompatibilním změnám a struktury komponent máme sestavené tak, aby se na základní opěrné body dalo z pohledu projektových vývojářů spolehnout.

Stejný mechanismus používáme nejen pro frontend, ale i pro celou administrační část. Libovolné místo naší aplikace lze obohatit nebo pozměnit dle požadavků konkrétního projektu a díky rozdílovému přístupu (DIFFu) si nezavíráme vrátka k upgradovatelnosti systému jako celku.

Pro představu…

Ačkoliv základní stavební kameny vznikly již před mnoha lety, způsob práce s UI neustále dolaďujeme, protože UI představuje velmi komplexní problém. V současné době tímto způsobem po dobu několika let aktualizujeme a udržujeme vyšší desítky řešení postavených na Edee.one a můžeme tedy na základě zkušenosti říci, že koncept jako takový se osvědčil. To, že naše řešení jsou z vizuálního pohledu dosti různorodá, se můžete přesvědčit třeba v sekci referencí.

Jan Novotný, Senior Application Developer

Mohlo by vás zajímat

Redesign webu pojišťovny Kooperativa (záznam přednášky z UX Day 2023)

Podívejte se na záznam přednášky o rozsáhlém redesignu, který jsme realizovali pro pojišťovnu Kooperativa. Těšte se na čísla, data i různé zajímavosti!

Celý článek

Na teambuilding v Krkonoších jen tak nezapomeneme!

Velký teambuilding obvykle pořádáme jednou za rok, takže si dáváme opravdu záležet. A ten letošní byl pořádně našlápnutý.

Celý článek

Přeskočit na hlavní nabídku