Radek Krejčí

Architektura

LydiaCMS jsem od začátku nechtěl stavět jako jeden pevně propojený balík funkcí. Potřeboval jsem systém, jehož části můžu vyvíjet, vydávat a případně použít i v jiném projektu. Proto jsem ho rozdělil do samostatných Composer balíčků, které se do aplikace zapojují prostřednictvím Nette DI.

Výsledkem je modulární monolit: za běhu jde stále o jednu aplikaci s jednoduchým provozem, ale její části mají vlastní repozitář, verzi a jasně popsané závislosti.

Jak architektura vznikala

První impuls přišel od kamaráda, který mě požádal o vytvoření webu s fórem. Projekt nakonec nepokračoval, ale zůstal po něm použitelný základ s uživateli a stránkami. Na něm jsem začal stavět obecnější CMS, který jsem mohl využít i pro další weby.

Prvním výrazně odlišným použitím byl Cluesport.com, pokus o metavyhledávač letenek, vstupenek a plánovaného ubytování na zahraniční fotbalová utkání. Z veřejného API jsem importoval zápasy, týmy, města a stadiony, z dalšího API nabídky letenek a data o vstupenkách jsem získával scrapingem. Ubytování zůstalo pouze v plánu.

Největší problém nebyl v samotném stažení dat, ale v jejich propojení a normalizaci napříč zdroji. Právě kvůli této potřebě vznikl samostatný balíček lydiacms-integration. Cluesport zároveň přinesl požadavky na vícejazyčnost a API.

Jak je aplikace rozdělená

LydiaCMS se skládá z 19 samostatně verzovaných Composer balíčků. Nejdůležitější z nich lze rozdělit do tří skupin:

  • Core zajišťuje spuštění aplikace, napojení na Doctrine, routování, oprávnění, lokalizaci a základ administrace.
  • Blog, Menu, Forms a Socials přidávají konkrétní funkce pro práci s obsahem.
  • Data, Integration, API a Backup řeší datové struktury, importy, programové rozhraní, zálohování a obnovu.

Každý balíček má vlastní repozitář a verzi. Mohu ho proto vyvíjet a vydávat samostatně; aplikace si přes Composer skládá konkrétní kombinaci verzí.

Zapojení modulů pomocí Nette DI

Každý modul je samostatný Composer balíček s vlastním rozšířením pro Nette DI. Rozšíření při sestavení kontejneru registruje služby modulu a jeho napojení na Doctrine. Podle potřeby přidává také routy, konzolové příkazy a další části aplikace. Zapojení modulu proto nevyžaduje ruční skládání jeho komponent v bootstrapu.

Administrace z metadat Doctrine

LydiaCMS obsahuje více než sto Doctrine entit. Psát pro každou z nich samostatně formulář, tabulkový přehled a filtry by znamenalo velké množství opakujícího se kódu. Z metadat Doctrine ORM proto generuji základní administrační rozhraní automaticky.

Vlastní komponenty vytvářím pouze tam, kde generované rozhraní nestačí — například pro stromovou správu menu nebo builder formulářů. Menší odlišnosti, jako nápovědu, placeholder nebo způsob vykreslení pole, nastavuji pomocí atributů u datového modelu.

Vícejazyčný obsah a stabilní adresy

Lokalizovaný záznam může mít samostatnou variantu pro každý jazyk. SEO údaje, jako jsou titulek, popis, obrázek a slug, se nastavují pro konkrétní jazykovou variantu, aniž by bylo nutné pro každý jazyk měnit databázový model.

Celá cesta stránky se skládá z jejího umístění ve stromu. Pokud se slug nebo umístění změní, původní adresa se uchová jako trvalé přesměrování na novou. Odkazy z vyhledávačů nebo externích webů tak nekončí chybou.

SEO záložka záznamu s jazykovými verzemi
SEO vlastnosti jsou součástí jazykové varianty záznamu.

Jednotný model oprávnění

Administrace i API používají stejný strom kontextů a stejná ACL pravidla. Oprávnění lze nastavit pro celý balíček, jeho konkrétní část nebo jediný záznam. Podřízené kontexty pravidla dědí a mohou je zpřesnit, takže nový modul nemusí vytvářet samostatný systém autorizace.

Pravidla rozlišují operace, například zobrazení, vytvoření, úpravu, smazání, změnu stavu nebo export, a také prostředí administrace, frontendu a API. Stejný mechanismus proto omezuje i správu obsahu pomocí AI.

Matice oprávnění v administraci LydiaCMS
Matice rozlišuje přímo nastavená a zděděná oprávnění v jednotlivých kontextech.

Co toto rozdělení přináší a co stojí

  • Projekt instaluje pouze moduly, které skutečně používá.
  • Každý modul lze verzovat a vydávat vlastním tempem.
  • Databázový model, administrace a oprávnění používají napříč moduly stejné principy.
  • Závislosti mezi balíčky jsou explicitně popsané v Composeru.

Tato architektura zachovává jednoduchý provoz jedné serverové aplikace. Cenou je nutnost hlídat kompatibilitu verzí, udržovat jasné hranice mezi společným jádrem a moduly a průběžně řešit režii vlastního balíčkového ekosystému.

Co bych dnes navrhl jinak

Jednu věc bych dnes oddělil dříve: základní vlastnosti entit bych navrhl tak, aby je bylo možné snadněji nahrazovat a rozšiřovat bez zásahů do pevně provázaného jádra. Právě tento problém mě vedl k práci na balíčku lydiacms-data a k většímu zájmu o agnostické datové modely a Metadata-Driven Architecture.

Dnes bych LydiaCMS směřoval k volnější, více API-first a headless architektuře. Jednotlivé části by měly jít snadněji měnit nebo nahradit a vlastní řešení by mělo vznikat hlavně tam, kde dostupné komponenty nestačí konkrétním požadavkům.

← LydiaCMS

Kontakt

Hledáte posilu do PHP týmu?

Pokud řešíte nový backendový projekt, integraci nebo potřebujete posílit vývoj existujícího systému, napište mi.