Myslel jsem, že typická webová aplikace se označuje za architekturu client-server. Ta se ale skládá z klienta, webového serveru a navíc databázového serveru, tedy 3vrstvá. Nebo tomu tak není? Proč?
Typické klient-server aplikace zažívaly svůj vrchol někdy před koncem tisíciletí, a byly to skutečně dvě vrstvy – klient byla třeba Windows aplikace napsaná v Delphi, která přes SQL přímo přistupovala do databáze (server). Webová aplikace určitě není typickou ukázkou architektury klient-server, protože tam těch klientů a serverů máte obvykle několik (webový server obvykle bude klientem aplikačního serveru nebo databáze). Můžete webové aplikaci samozřejmě také říkat klient-server architektura, protože tam nějaké servery a nějací klienti jsou, a abyste to odlišil od monolitické aplikace. Ale je podle mne méně matoucí držet se zažitého používání pojmů a označovat webové aplikace za vícevrstvé.
Možná je to pořád tím, že si pod každou vrstvou představuju oddělený HW...
Což je dobrá představa a myslím, že je to jednodušší pro pochopení. To, že si pak někdo nacpe webový server a databázi na jedno železo, je speciální případ. Ale když si představíte, že máte na jednom počítači webový prohlížeč, ten komunikuje s webovým serverem, který generuje HTML kód, webový server komunikuje s aplikačním serverem, kde je aplikační logika (poskytovaná přes nějaké API), a aplikační server komunikuje s databázovým serverem, máte tam krásně i hardwarově oddělené čtyři vrstvy.
Nakreslil jsem obrázek webové aplikace (tenký klient). Chápu tedy, že může být současně MVC na klientovi (asi tedy nějaký JS MVC framework) a také na webovém serveru (např. MVP Nette framework), jedná se tedy stále o prezentační vrstvu...
Ano, u čtyřvrstvé architektury, kde je tenký klient a prezentační server je oddělený od aplikačního, je prezentační logika často rozdělená mezi prezentační server a tenkého klienta. Případně může být opravdu tenký klient úplně bez logiky – to jsou klasické webové formuláře, kde něco vyplníte, odešlete na server, tam se to zpracuje a server vytvoří HTML stránku s odpovědí. Je to sice architektonicky čisté, ale odezva na každou akci uživatele pak jde přes server, což je pomalé (projevuje se tam latence sítě).
Nicméně nerozumím tomu, že Model v MVC/MVP je součástí prezentační vrstvy, když obsahuje business logiku...
Model v MVC neobsahuje business logiku, obsahuje data pro prezentační vrstvu. U klasické formulářové webové aplikace je model v podstatě to, co se odešle typicky jako obsah POST požadavku na server (ze serveru do prohlížeče to jde typicky jako součást webové stránky, tam je tedy model a view pro přenos spojeno dohromady). Nebo pokud je to webová aplikace komunikující pomocí JSON zpráv, jsou modelem ty JSON zprávy.
Když budete mít třeba webovou stránku zobrazující údaje o nějaké osobě, budete tam mít model, který bude obsahovat jméno, příjmení, datum narození, adresu. Bude obsahovat jen tahle data, logika tam nebude vůbec žádná. Případně v implementaci (nějaké PHP nebo Java třídy, ve skutečnosti jsou to ale jen struktury bez jakéhokoli výkonného kódu) tam třeba budou nějaké jednoduché validace, což je ale vlastně porušení těch klasických vrstev (protože „správně“ by validaci měla dělat až business logika na aplikačním serveru). Tenhle prezentační model se pak mezi prezentační a aplikační vrstvou překlopí na doménový model (pokud „náhodou“ nejsou stejné), a teprve nad doménovým modelem pracuje ta business logika (která třeba zkontroluje, zda daný uživatel má právu tuhle osobu editovat, ověří, že datum narození není v budoucnosti, zda nevzniká duplicitní záznam o osobě v databázi, normalizuje adresu apod.).
Zkusím to ještě popsat opačně, proč vzniklo MVC. Bez MVC můžete mít na formuláři tři políčka – pro rodné číslo, datum narození a pohlaví. Každé to formulářové políčko si pamatuje svou hodnotu, a pak musíte naprogramovat chování pro ta jednotlivá políčka. Když uživatel změní rodné číslo, zkontrolovat jeho formát, vypočítat datum narození a zapsat ho do příslušného políčka, vypočítat pohlaví a zapsat ho do příslušného políčka. Když uživatel změní datum narození a je vyplněno rodné číslo, zobrazit dotaz, že se pravděpodobně jedná o cizince bez rodného čísla, a zda chce rodné číslo vymazat. Pokud zvolí, že ne, vrátit datum narození na hodnotu vypočtenou z rodného čísla. Když ale vymažete rodné číslo, nesmíte zapomenout i vymazat pohlaví. A takhle se to postupně řetězí a při každé změně v nějaké komponentě na formuláři musíte myslet na to, co všechno to může ovlivnit jinde. Pak třeba k políčku pro zadání data narození přidáte tlačítko pro výběr z kalendáře, a musíte zajistit, aby se ty dva údaje synchronizovaly a při změnách v kalendáři se provádělo to samé, jako při změnách v textovém políčku. Postupně tak z toho vznikne dlouhý kód plný různých ifů, logika se tam duplikuje a za chvíli se to celé bude chovat zmateně (třeba když změníte dvě hodnoty v jednom pořadí, bude na nějakém třetím políčku výsledek jiný, než když je změníte v obráceném pořadí).
Proto vzniklo to MV, kde máte to zobrazení (view) a model oddělené. Prostě jenom řeknete, že v tomhle políčku je datum narození, a nemusíte v pohledu řešit, co se má stát při jeho změně a kdy se má naopak aktualizovat – protože o to se stará model. Samotné MV by pak stačilo pro jeden formulář, jenže typicky jich máte víc a potřebujete určit, že když aplikační logika zjistila, že je v datech něco špatně, má se zobrazit zase ten původní formulář se změněnými údaji a validační hláškou, ale když aplikační logika data zpracovala (a třeba uložila do databáze), má se uživateli zobrazit jiná stránka třeba s poděkováním za registraci. A to řeší C – controller.
V tom případě, který jsem popsal, je opravdu část té business logiky (jaký je vztah mezi rodným číslem, datem narození a pohlavím) vytažena až k tomu modelu v MVC, ale to neznamená, že je to architektonicky součástí modelu. Architektonicky čisté by bylo vzít ten model z prezentační vrstvy, odeslat ho na aplikační server, tam třeba z toho rodného čísla vypočítat datum narození a pohlaví a odeslat to zpátky na klienta. Jenže za to vám uživatel nepoděkuje, když na to bude muset čekat a za tu dobu by to datum narození napsal sám.