Včera jsem se zúčastnil diskuse asp.net MVC Best Practices pořádanou v místní pobočce Microsoftu. Sešlo se tam mnoho zajímavých lidí, nejen ti, kteří dělají v .NETu, ale i lidé, kteří vyvíjejí na jiných platformách, ale používají MVC. Já sám jsem se do diskuse moc nezapojoval, protože můžu srovnávat pouze ASP.NET MVC a ASP.NET WebForms, které tam převážná část diskutujících neznala, ale rád bych tady zareagoval na pár “argumentů” pro MVC, které mi až tolik jako argumenty nepřijdou (ale je to jen proto, že to srovnávám s ASP.NET WebForms). Kdybych ASP.NET nezažil a pracoval dřív jen v PHP, pak bych rozhodně MVC uvítal. Jenže já jsem mezi tím absolvoval WebForms a právě proto se mi MVC (a teď nemyslím jen ASP.NET MVC, ale MVC vzor obecně) nelíbí.
Následující odstavce jsou můj osobní názor na MVC, nenapsal jsem toho v něm tolik, ale myslím, že dost na to, abych si o tom názor udělal. Není to rozhodně špatný přístup architektury aplikací, nicméně v ASP.NET WebForms se mi dělá lépe.
Jen stručně - jak fungují WebForms a jak funguje MVC
ASP.NET WebForms vznikly zhruba před deseti lety a byly svým způsobem revoluční. Oproti klasickému spaghetti kódu se v ASP.NET do stránky daly serverové komponenty (které se zapsaly normálně do HTML stránky) a fungovaly na nich normálním způsobem události, jak je známe třeba z WinForms aplikací. Stav komponent bylo třeba nějak zachovat a někam uložit, vzhledem k tomu, že protokol HTTP je bezstavový, zařídilo se to tak, že celá stránka je formulář a obsahuje mimo jiné skryté pole __VIEWSTATE, které tyto stavové informace drží.
Jednoduše lze oddělit vzhled stránky (markup se serverovými komponentami) a aplikační logika (code behind, který obsahuje obsluhy událostí a samozřejmě mnoho vlastních tříd, který zajištují datovou vrstvu a bussiness logiku; v code behindu by měly být jen obsluhy událostí, manipulace s komponentami a volání funkcí z bussiness vrstvy).
MVC funguje trochu jinak - celá aplikace se dělí na 3 striktně oddělené vrstvy - Model, který obsahuje datovou vrstvu a bussiness logiku (např. zakládání objednávek) se vším všudy, např. validace (jméno objednávky musí být vyplněno). Další vrstvou je Controller, který zpracovává vstupy od uživatele (reaguje na HTTP požadavky a rozhoduje, co se má stát; volá metody z bussiness vrstvy, vytáhne příslušná data z databáze atd.), řeší také oprávnění uživatele, a View, který dostane data od Controlleru a nějakým způsobem je zobrazí (např. vygeneruje HTML). To je velmi stručně řečeno, pokud chcete podrobnější výklad, vřele doporučuji články o MVC od Borka Bernarda, které vyšly na webu http://zdrojak.root.cz.
ASP.NET a tlačítko Zpět
Jeden z argumentů, který včera zazněl, a ke kterému jsem se i vyjadřoval, bylo, že “ASP.NET WebForms aplikace nectí protokol HTTP, že se používá POST místo GETu, že správně nefunguje tlačítko Zpět atd.”
Ano, je pravda, že ASP.NET zachází s HTTP jinak, než ostatní technologie. Pokud je programátor prase a nepozná rozdíl mezi komponentami HyperLink a LinkButton (HyperLink je klasický odkaz na jinou URL, tedy potažmo GET, a LinkButton je odkaz, který pomocí klientského skriptu odesílá formulář, dělá PostBack, potažmo tedy POST), pak se může velmi snadno stát, že i pro operace, pro které se má používat GET, se použije PostBack. Tuhle pitomost dělá co já vím ještě komponenta Calendar, kterou ale stejně nedoporučuji používat, protože generuje strašné HTML.
Stejně tak nekorektní fungování tlačítka Zpět, samozřejmě že v ASP.NET jde napsat aplikace, kde Zpět nebude fungovat správně, ale chce to dost námahy a stane se to akorát lidem, kteří absolutně nechápou, jak HTTP funguje. Tohle prostě není problém ASP.NET WebForms, ale problém programátora. Programovat v ASP.NET tak, abychom správně používali POST a GET nedá žádnou práci navíc, je to naprosto přirozené.
MVC a lepší testování
Obecně si myslím, že většina programátorů v ČR ještě nedospěla k tomu, že kód by měla nějak systematicky testovat. Ano, testujeme všichni tím, že alespoň jednou zkusíme, jestli aplikace jde spustit. U větších aplikací se ale většinou vyplatí strávit čas vytvořením sofistikované sady testů (což mimochodem není nic jednoduchého). “Aplikace v MVC se testují daleko lépe než ostatní aplikace.”
Pokud je aplikace zbastlená (tzn. je psaná pomocí spaghetti kódu, nemá pořádnou datovou vrstvu, nebo se píše v ASP.NET a i komplexní logika je v code behindu), nemá stejně smysl testovat a nikdo ji ani testovat nebude. Pokud je to aplikace malá, všeho všudy o deseti stránkách, nemá většinou cenu ani nějakou datovou a business vrstvu dělat (v případě, že víme, že se na aplikaci nebude nic moc nabalovat, pokud ano, je nutné s tím počítat dopředu).
Pokud je aplikace větší a testovat by se měla, tak má stejně nějak vyčleněnou datovou a business vrstvu (model). Ten vypadá víceméně stejně bez ohledu na použitou technologii a testovat se bude také stejně. Pro testování funkčnosti aplikace se používají nějaké automatizované web testy (simulace požadavků a sledování výsledků podle předem daného scénáře), skuteční testeři, kteří aplikaci fyzicky proklikají, případně další způsoby, ale ty jsou také nezávislé na použité technologii a jdou aplikovat prakticky všude.
Co v MVC zbývá? Model otestovaný máme, View také, chybí ještě Controller. Je ten potřeba testovat? Podle mého názoru v drtivé většině případů ne. To, jestli správně funguje autentizace, se dá ošetřit (a také se to tak většinou dělá) spíš pomocí webtestů, a v Controlleru stejně děláme jen to, že zavoláme něco z Modelu, který upraví databázi (což už máme otestované), a pak vytáhneme data a předáme pohledu (což se otestuje webovou částí). Jakákoliv složitější logika nemá stejně v Controlleru co dělat a patří do modelu. Co se tam tedy testuje lépe?
MVC se hodí na všechny typy webových aplikací
Tohle je argument, který se mi nelíbí ani trochu, není ničím podepřen. Pokud má znamenat, že se v MVC dají napsat všechny webové aplikace, pak nezbývá než souhlasit (není ani divu, kdyby MVC nebylo turingovsky úplné, tak by se s ním určitě ani nikdo nezabýval). Problém je v tom, že pořád mám v MVC při programování víc práce (a není to tím, že bych s tím neuměl nebo dělal nějaké koncepční chyby).
Demonstrováno to bylo včera na pěkném příkladu - mám nějakou stránku, na kterou chci přidat komentáře. V Modelu mám vše již nachystané, mám napsanou nějakou komponentu, která komentáře zobrazí.
V ASP.NET WebForms to můžu udělat jednoduše - komponenta prostě sáhne do databáze, vytáhne komentáře a zobrazí je. Není to nic proti ničemu, neporušujeme tím žádný koncept, nemixuji vzhled a logiku dohromady (ve stránce řeknu, co tam má být a jak se to má udělat, o to se vůbec nestarám; v komponentě mám definován vzhled, logika je v modelu a v code behindu jen vytáhnu data a plácnu je do nějakého Repeateru). Vše tedy spočívá jen v tom, že přidám do stránky komponentu.
V MVC musím sáhnout na víc míst - komponentu pro View mám sice již napsanou, ale ona si nemůže sáhnout do modelu (respektive může, ale je to proti architektuře MVC, má to i svá logická opodstatnění, View má být tupá šablona, která jen zobrazuje data, nemá obsahovat žádnou logiku, data má dostat od Controlleru). Musím tedy upravit View (přidat tam na správné místo tu komponentu), a navíc musím upravit Controller, aby předával do View data pro tu komponentu, sama si je vzít nemůže. Je nutné si uvědomit, že Model není databáze, model je soustava tříd, která poskytuje funkce pro práci s daty, samotnou business logiku (třeba evidenci dokumentů, přidávání, odebírání, schvalování, připomínkování atd.). Jak to komunikuje s databází a co je to vlastně za databázi (SQL nebo něco jiného), to už je z hlediska architektury nepodstatné.
Tady je trochu zádrhel - zatímco v ASP.NET z komponenty do modelu sahat můžu, v MVC bych neměl. Já bych správně neměl ani v ASP.NET, ne kvůli konceptu, ale proto, že si tím můžu solidně zavařit a na první pohled není vidět, kolik dotazů do databáze bude a jak to bude s výkonností. Pokud ty komentáře chci zobrazit na třech místech a jedné stránce, tak v ASP.NET si do modelu komponenta sáhne třikrát (když to udělám hloupě; když ne, tak si to model nacacheuje, ale to nemusí být samozřejmost, navíc ne vždy je to takto jednoduché), v MVC jen jednou a všechny “dotazy a žádosti pro data” mám na jednom místě - v Controlleru. Je tedy hned vidět, co stránka bude dělat, je to průhlednější.
To je ale právě to, co mi na tom vadí. Ve WebForms můžu controller jakoby “poskládat z částí” - část je pro komentáře, část je pro články. Pokud budu články zobrazovat na jiné stránce, musím vytahování a předávání šabloně mít na více místech a mám opakující se kód. Komponenty ve WebForms jsou oproti MVC silnější, nahrazují opakující se kód v šabloně i v “controlleru”.
Ano, MVC asi víc programátory vede k tomu, aby psali čistěji. Controller je na jednom místě a není rozcamdaný na části v komponentách. Ve WebForms může neznalý člověk nadělat víc škody (ViewState je sice geniální věc, ale musí se používat tak, aby sloužil a rozhodně ne bezhlavě), v MVC se zase víc nadře a dá to víc práce. Na druhou stranu konkrétně v ASP.NET MVC nejde nějak rozumně zabránit programátorům, aby do View nedávali kód a logiku, která tam nepatří, nejde jim ani pořádně zabránit v tom, aby z View sahali do databáze, což jde proti konceptu a bohužel se to bude tak zneužívat, síla zvyku z PHP je síla zvyku a lidé, kteří dřív bastlili v PHP (a těch je hodně), budou teď bastlit v MVC.
Osobně mám ale raději WebForms. Možná to není tak čisté a v MVC je kontrola nad výstupním kódem, není tam ViewState, ale včerejší diskuse mě nepřesvědčila, spíš utvrdila v tom, že kdo zkusil a ovládl WebForms (tzn. naučil se je pořádně a do hloubky, což není záležitost jednoho týdne), na MVC pravděpodobně nepřejde.
Konkrétně ASP.NET MVC je podle mě krok zpět oproti WebForms, takový návrat k ASP či PHP, nelíbí se mi koncept View jakožto tupé šablony. Právě kvůli tomu jsem utekl od PHP k ASP.NET WebForms a budou muset vymyslet něco mnohem lepšího, aby mě přesvědčili. V každé technologii se dá prasit a v každé technologii se dá psát pořádně.
Odbočka - moje videotutoriály ASP.NET na MSTV.cz a best practices
Pokud sledujete můj videoseriál Začínáme s ASP.NET, který je určen pro lidi, kteří se chtějí s ASP.NET seznámit, nedělejte si iluze, že po jeho shlédnutí budete psát pořádné webové aplikace. ASP.NET se nedá naučit během 20x15 minut, tutoriály jsou k tomu, aby se mohli začátečníci s technologií seznámit a pochopit její principy, které jsou dost odlišné od technologií jiných.
Malé webové aplikace samozřejmě jde klidně psát tak, jak se to v tutoriálech učí, pomocí SqlDataSource atd. Tyto komponenty umožňují napsat jednoduchou stránku za 10 minut a bude to fungovat. Velké aplikace mají samozřejmě datovou a business vrstvu, takže se dají používat stejné komponenty (GridView, Repeater atd.), ale plnit se budou z modelu, ne přímo z databáze. Navíc SqlDataSource jde také dost proti konceptu - v definici vzhledu se definuje i aplikační logika - např. SQL dotazy.
Pokud se ale naučíte, jak funguje SqlDataSource, pochopíte styl a způsob, jakým ASP.NET funguje, pro začátečníky je to ideální. Je to také jediný přístup, který je všude stejný - v každé firmě se velké ASP.NET aplikace píšou jinak, někde mají zakoupené jiné komponenty, model všude vypadá jinak, není žádný společný základ. Navíc nikdo po začátečnících nemůže chtít, aby hned psali profesionálně vyvedené aplikace, důraz se klade na vysvětlení a pochopení principu, protože to je to důležité. Bez této znalosti pak programátoři dělají ty nejhorší chyby.