Slevový týden - Květen Office week
Pouze tento týden sleva až 80 % na e-learning týkající se MS Office
30 % bodů zdarma na online výuku díky naší Slevové akci!

MVC architektura

MVC je velmi oblíbený architektonický vzor, který se uchytil zejména na webu, ačkoli původně vznikl na desktopech. Je součástí populárních webových frameworků, jakými jsou např. Zend nebo Nette pro PHP, Ruby On Rail pro Ruby nebo MVC pro ASP .NET. Osobně si bez něj (nebo nějakého podobného principu) nedokáži představit složitější web.

Motivace

Základní myšlenkou MVC architektury je oddělení logiky od výstupu. Řeší tedy problém tzv. "špagetového kódu", kdy máme v jednom souboru (třídě) logické operace a zároveň renderování výstupu. Soubor tedy obsahuje databázové dotazy, logiku (např. PHP operace) a různě poházené HTML tagy. Vše je zamotané do sebe jako špagety.

Kód se samozřejmě špatně udržuje, natož rozšiřuje. Je špatně highlightovaný, protože si s ním IDE neví rady, HTML není správně naformátováno, ztrácíme se v jeho stromové struktuře. Naším cílem je, aby zdrojový kód s logikou vypadal jako zdrojový kód (např. PHP) a výstup vypadal jako HTML stránka s co nejmenší příměsí dalšího kódu.

Komponenty

Celá aplikace je rozdělena na komponenty 3 typů, hovoříme o Modelech, View (pohledech) a Controllerech (kontrolerech), od toho MVC. Označení pohled se budu snažit vyhýbat, protože mi přijde matoucí, že takto přeložené nekoresponduje s označením V. Neexistuje žádná striktní definice architektury a tak se můžete setkat s více výklady. Zaměřil jsem se na ten nejrozšířenější.

Komponenty jsou samozřejmě třídy, odděděné z abstraktních tříd Model, View a Controller. Pojďme si jednotlivé komponenty nejprve popsat.

Model

Komponenta model z MVC architektury Model obsahuje logiku a vše, co do ní spadá. Mohou to být výpočty, databázové dotazy, validace a podobně. Model vůbec neví o výstupu. Jeho funkce spočívá v přijetí parametrů zvenku a vydání dat ven. Zdůrazním, že parametry nemyslím URL adresu ani žádné jiné parametry od uživatele. Model neví, odkud data v parametrech přišla a ani jak budou výstupní data zformátována a vypsána.

Pokud používáme ORM (Objektově-Relační Mapování), korespondují modely přímo s databázovými tabulkami. Máme tedy model Uzivatel, Komentar nebo Clanek. Instance modelů obsahují samozřejmě atributy z databáze. Instance modelu Uzivatel má atribut jméno. Třídě můžeme definovat instanční metody, např. takovou, která vypočítá věk uživatele podle jeho data narození. Metody týkající se obecně uživatelů (tedy třídní) často vkládáme do modelu jako statické, např. oveření správné délky a znaků hesla (tedy jeho validaci, protože heslo ověřujeme ještě předtím, než je instance uživatele vytvořena a zároveň s uživatelem logicky souvisí).

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Pro programování bez ORM můžeme využít principu manažerů. Modely máme např. UzivatelManazer, KomentarManazer nebo ClanekManazer. Obsahují metody k výběru potřebných údajů z databáze, např. metodu k výběru přezdívky a informací o uživateli při vypsání jeho profilu. Jinou metodou bychom vybírali otisk hesla z databáze, abychom ověřili přihlášení, další by nám vrátila třeba články uživatele. V manažeru zároveň najdeme pomocné metody k práci s uživateli, např. ono ověření správné délky a znaků hesla.

Nyní máme představu, co model vykonává, pojďme se podívat na pohled.

View

Komponenta view z MVC architektury Pohled (View) se stárá o zobrazení výstupu uživateli. Nejčastěji se jedná o phtml šablonu, obsahující HTML stránku a tagy nějakého značkovacího jazyka, který umožňuje do šablony vkládat proměnné, případně provádět iterace (cykly) a podmínky. Pro PHP nemá příliš velký smysl používat značkovací jazyk, jelikož je samo značkovacím jazykem a umožňuje takový styl zápisu kódu, aby struktura HTML stránky zůstala zachována. Pohled uzivatel tedy vypíše detaily o uživateli, pohled clanek vypíše obsah článku.

Pohledů máme mnoho, např. pro funkcionalitu s entitou uživatele: uzivatel_regis­trace, uzivatel_prih­laseni, uzivatel_profil a podobně. Pohled uzivatel_profil je ale již společný všem uživatelům a jsou do něj posílána různá data, vždy podle toho, koho zrovna zobrazujeme. Tato data jsou poté dosazena do HTML elementů šablony.

Šablony lze samozřejmě vkládat do sebe, abychom se neopakovali (šablona s layoutem stránky, šablona s menu a šablona článkem).

View není jen šablona, ale zobrazovač výstupu. Obsahuje tedy minimální množství logiky, která je pro výpis nutná (např. kontrola, zda si uživatel vyplnil prezdívku před jejím vypsáním nebo cyklus s komentáři, které se vypisují).

View podobně jako Model vůbec neví, odkud mu data přišla, stará se jen o jejich zobrazení uživateli.

Controller

Komponenta controller z MVC architektury Controller je nyní onen chybějící prvek, který osvětlí funkčnost celého vzoru. Jedná se o jakéhosi prostředníka, se kterým komunikuje uživatel, model i view. Drží tedy celý systém pohromadě a komponenty propojuje. Jeho funkci pochopíme z ukázky životního cyklu stránky. Opět existuje mnoho různých přístupů, nejčastěji má každá entita jeden controller, máme tedy UzivatelController, ClanekController a tak podobně.

Životní cyklus stránky

Životní cyklus zahajuje uživatel, který zadá do prohlížeče adresu webu a parametry, kterými nám sdělí, kterou podstránku si přeje zobrazit. Budeme chtít zobrazit detail uzivatele s id 15. Udělejme si ukázku URL adresy:

http://www.domena.cz/uzivatel/detail/15

Požadavek jako první zachytí tzv. router. Ten podle parametrů pozná, který controller voláme. Jistě bychom vymysleli mnoho způsobů, jak jméno controlleru poznat, nejjednodušší se zdá být uvést ho jako 1. parametr. Zde je tedy zavolán controller UzivatelContro­ller., kterému jsou předány parametry detail a 15.

Daný controller podle parametrů pozná, co se po něm chce, tedy že má zobrazit detail uživatele. Zavolá model, který uživatele vyhledá v databázi a vrátí jeho údaje. Dále zavolá další metodu modelu, která např. vypočítá věk uživatele. Tyto údaje si controller ukládá do proměnných. Nakonec vyrenderuje view (pohled). Název pohledu poznáme podle akce, kterou provádíme. View jsou předány proměnné s příslušnými daty. Controller tedy poslechl uživatele, obstaral podle parametrů dotazu data od modelu a předal je view.

View přijme data od controlleru a vloží je do připravené šablony. Hotová stránka je zobrazena uživateli, který často o celé této kráse ani netuší :)

Celou situaci můžeme znázornit diagramem:

MVC architektura

Získali jsme tedy oddělení logiky od výstupu, view jsou jako HTML, modely zas v našem skriptovacím jazyce (např. v PHP). Dosáhli jsme přehlednosti kódu, který je logicky rozčleněný.

MVC architektura nám usnadňuje i myšlení při vývoji projektu. Když píši logiku, patří do modelu, formátování a stylování výstupu řeším v šabloně, to co uživatel chce z parametrů zjišťuji v controlleru. 3 různé problémy na 3 různých místech, oddělené tak, aby do sebe nezasahovaly a nedělaly nám vývoj složitější.


 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
32 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Všechny články v sekci
Objektově orientovaná analýza a návrh softwaru
Aktivity (3)

 

 

Komentáře

Avatar
Andrej Farkaš:24.10.2013 7:46

Super článok. Už len to dostať do praxe :-)

Odpovědět
24.10.2013 7:46
Live. Love. Learn.
Avatar
gullyCZ
Člen
Avatar
gullyCZ:12.7.2014 6:37

Výborný článek. Díky za něj!

 
Odpovědět
12.7.2014 6:37
Avatar
Marian Benčat:28.1.2016 14:29

Bohužel nemohu s některýma věcma souhlasit... s některýma nesouhlasím dosti hrubě.

"Pokud používáme ORM (Objektově-Relační Mapování), korespondují modely přímo s databázovými tabulkami. Máme tedy model Uzivatel, Komentar nebo Clanek. Instance modelů obsahují samozřejmě atributy z databáze. "

Modely rozhodně neodpovídají databázi. Model je logika aplikace a ta by měla být na 100% odstíněna o tom, jak jsou uloženy data - většinou se k tomu používá nějaký typ "storu" - repozitáře. Takže model NESMÍ vědět o tom, jak jsou data uložena - z pohledu modelu , on využívá pouze metody určitého abstraktního storu, ten je poté implementován, nějakou DAL vrstvou.

Model je většinou implementován jako nějaká fasáda - servisa, chcete-li. Ta servisa poté využívá storu k operacím, které jsou spokjeny s daty, (logika zustava v modelu - ve fasádách - ta si také řídí UOW).

Pokud chcete říct, že model odpovídá databázi, dopouštíte se hodně chyb a přivede vám to opravud hodně problémů, především co se týče testování aplikace, modularity a responsibility.

Vy se snažíte používat tzv. Rich Domain model, který je zcela správně co se týče objektového návrhu, ale porušuje defakto úplně všechny SOLID pravidla a to opravdu velmi velmi silně. Oproti tomu Aenemic domain model (který nedodržuje stirktně OOP - plní SOLID v každém ohledu naprosto ideálně). Aenemic model je právě rozdělení aplikace na N-tier aplikaci (Frontend, servisy, dal, db,..).

Je to samo o sobě ironie - SOLID /což je poučka jak dělat OOP aplikace /, zde dokazuje, že je lepší použít non-OOP přístup, protože OOP samotný SOLID narušuje.

Doporučuji vám opravdu velmi silně NErvat do modelu všechno (db přístup, story, logiku), přiděláte si tím hodně problémů a nic vám to nepřinese, - tedy kromě toho, že se budete moci poplácat, že ej to správně OOP.

Na závěr bych rád řekl, že oop by jste měl používat tam kde vám nehází klacky pod nohy (nebo nebude házet).

*ps - modely by rozhodne nemely obsahovat 1:1 atributy jako jsou v databazi, to je hloupost. Jedna se opet o implementacni detail databaze, ktery vy vedome leakujete z vrstvy DALu.

Editováno 28.1.2016 14:31
Odpovědět
28.1.2016 14:29
Totalitní admini..
Avatar
JOF
Tým ITnetwork
Avatar
Odpovídá na Marian Benčat
JOF:28.1.2016 23:15

Ahoj,

spoustě těch slov vůbec nerozumím :-( , ale to je samozřejmě mojí neznalostí. Chtěl bych jen říct, že mi terminologie v MVC nepřijde úplně jednotná. Modelem v DAL se často označují třídy, které jsou 1:1 s tabulkami v DB (např. u code first přístupu). Modely v business vrstvě, nad kterými se provádí veškerá logika, už nemusí být 1:1 s DB. Logika aplikace také nemusí být přímo v modelech, ale třeba v servisách nebo managerech. V aplikační vrstvě jsou mody často DTOčka z business vrstvy. Jako modely zde také bývají označovány ViewModely, které jsou předávány pohledům.

Takže já zase nesouhlasím s tvrzením, že "Model je logika aplikace..."

 
Odpovědět
28.1.2016 23:15
Avatar
Odpovídá na JOF
Marian Benčat:28.1.2016 23:25

souhlasim ze je to zavadejici, S necim souhlasim, s necim ne.

I U code first je v DALu entita správně svobodná od Databáze - přináší to spoustu výhod, běžně tedy se pro code first použije tedy nějaké DTOčko, ale to neví o tom, že existuje "nějaká databáze" - je to obecný objekt - přepravka pro data, které se pohybují po aplikaci. a konfigurace (mapovani) se deje mimo toto DTO. tedy DTO muze byt uplne jine nez databaze (a je tomu spravne) Já osobně za MODEL v MVC považuji pouze a jenom "core" - servisy, fasády..ale to je muj nazor.

Ze širokého hlediska lze skutečně říkat model 10ti ruznym vecem...Pokud je ale v clanku myslen MODEL = entita v DALu, tak to nic nemeni na tom, ze je spatne pokud je natvrdo navazana na konkretni tabulky ;-) od toho je to Code first - nás "nezajímá" jak je to v DB, my prostě máme objekty a jak se to udělá na pozadí - to je na konfiguraci.

Odpovědět
28.1.2016 23:25
Totalitní admini..
Avatar
Odpovídá na Marian Benčat
Jakub Šárník:29.1.2016 0:05

Já sám třeba nesnáším tu dnešní přílišnou komplexnost těchto věcí. Souhlasím s tím, že model by vůbec neměl vědět o databázi a líbí se mě přístup Entity Frameworku, kde jsou modely obyčejné třídy a o databázi se stará DbContext (podle dnešního názvosloví nejspíš repozitář). Nechápu, proč i takhle se vytváří různé viewmodely, dokonce někteří dělají reposizář ještě nad DbContext... Na druhou stranu chápu data transfer objecty, když jsou nutné pro předávání specifických dat nebo na odstranění kruhové závislosti např. při serializaci na JSON (to nastane když závislý objekt odkazuje na svého rodiče a ten zase odkazuje zpět např. přes nějakou kolekci)

 
Odpovědět
29.1.2016 0:05
Avatar
Odpovídá na Jakub Šárník
Marian Benčat:29.1.2016 8:21

Ono je to naopak dosti pochopitelné. View modely mít naopak chcete. Jednak vám automaticky osetri věci jako je overbinding a také dp nich nandate logiku pro ui. Já třeba mám vlastní knihovnu, že na základě view modelu se mi automaticky generuje formulář a to na bootstrapu, poháněný angularem, ng-messages a bootstrap tooltipem a plno dalších věcí. Tyto informace třeba v dto nemají co dělat.na ani tam být nemůžou.. Dto máte v nějaké class library ,,shared" tam prostě mít referenci na mvc nemůžete.

Ohledně repozitory.. Db context je sám o sobě kombinace repozitare a uow, ale toto je věc kterou rozhodně nechcete exposovat z dálu.. Rozhodně nechcete vracet něco jako dbset.

Odpovědět
29.1.2016 8:21
Totalitní admini..
Avatar
Odpovídá na Marian Benčat
Jakub Šárník:29.1.2016 11:06

Tu poslední větu jsem nějak nepochopil :-D. Ale já třeba nevidím problém v předávání modelu to pohledu (v ASP.NET), když k tomu i VS přímo vybízí.

 
Odpovědět
29.1.2016 11:06
Avatar
Odpovídá na Jakub Šárník
Marian Benčat:29.1.2016 11:21

Omlouvám se, psal jsem to už v posteli a druhou rukou hladil přítelkyni na zádech, takže jsem nemohl efektivně odepisovat :D

Ano..i základní templaty bohužel mají složku models, do které rvou například modely z IdentityFrameworku a i viewmodely, vše do složky models.. dokonce i ViewModels jsou nazývány také jako View Specific Models,.. což je i samotným microsoftem považováno - že udělali blbost.

Předávání modelu do pohledu je špatně z několika důvodů, kde některé hned zmíním:

  1. Pokud chcete ovlivnit výslednou stránku - view a chcete to ovlivnit na základě view modelu, musíte upravit model - což je absolutně špatně.. model jsou dat aaplikace, proč bych je já měl modifikovat na zákaldě toho, že chci mít jiné view?
  2. pokud je model něco co má odpovídat DB, tak máte zaděláno na velký průser s nutností velkého refaktoringu kódu
  3. máte automaticky vyřešený problém overbindingu a validace
  4. V DTO nesmíte mít informace o UI validaci / zobrazení atp. pokud to tam máte, předem jste odkázal celý svůj solution na to, že na frontendu bude MVC... jelikož musíte použít různé data anotace a další reference na MVC

K poslendí větě:

Ohledně repozitory.. Db context je sám o sobě kombinace repozitare a uow, ale toto je věc kterou rozhodně nechcete exposovat z dálu.. Rozhodně nechcete vracet něco jako dbset.

DBContext entity frameworku je implementován jako repository pattern (DBSet) a UOW (SaveChanges()).
Pokud máte le složité dotazy, dáte je buďto do repozitáře, nebo do query objectu (ideálně máte prostě generický repozitář, od kterého dědíte a v podědělné třídě můžete mít další metody - queries). Co ale nesmíte udělat je mít v repozitáři něco takovéhoto:

public IQueryable<Article> GetMyArticle () {
return dbcontext.Arti­cles.Where(..­.);
}

Jelikož vracíte IQueryable db contextu - tzv. leakujete do vyšší vrstvy (servisa) informace o DALu (DBContext) = timto jste se predem omezil na to, ze pokud budete chtit DAL zamenit za nejaky mockup (testovani), nebo udelat nejakou strategy / pripadne zmenit implementaci,...

Tak musíte reimplementovat jak kolík ;-)

Odpovědět
29.1.2016 11:21
Totalitní admini..
Avatar
Odpovídá na Marian Benčat
Jakub Šárník:29.1.2016 11:28

Ohledně repository, na stackoverflow i jedne teda panuje názor, že vytvářet nějakou vrstvu nad DbClntextwm je zbytečné a nedoporučuje se. A ohledně DTO a ViewModelu... nikdy jsem neviděl žádnou implementaci, ani něco jako getting started takže si to moc nedovedu představit, například jak by měl ovlivňovat view. Pro mě je všechna prezentační logika ve viewu a v modelu data/business logika.

 
Odpovědět
29.1.2016 11:28
Avatar
Odpovídá na Jakub Šárník
Marian Benčat:29.1.2016 11:36

Píšete testy?

Odpovědět
29.1.2016 11:36
Totalitní admini..
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Odpovídá na Marian Benčat
Marian Benčat:29.1.2016 11:38

Já si pozadam o to mít možnost tu přidat článek a odpovim na vše tam. Potom sem dám odkaz

Odpovědět
29.1.2016 11:38
Totalitní admini..
Avatar
Odpovídá na Marian Benčat
Jakub Šárník:29.1.2016 11:43

Ok, I když mě jde spíš o krátklu ukázku praktické implementace všeho tohohle overengineeringu :-P (tak asi to k něčemu bude no, ale já mám rád věci jednoduché). A já osobně jsem ještě testy psát nemusel ale vím teoreticky jak se píšou a jak fungují.

 
Odpovědět
29.1.2016 11:43
Avatar
Odpovídá na Jakub Šárník
Marian Benčat:29.1.2016 11:48

Pokud použijete slovo nemusel u testu, je to trošku nepochopení testů. To není že musíte, ale máte a pomůže vám to. Nechci ale na vás útočit a příklady vám v článku dám.

Odpovědět
29.1.2016 11:48
Totalitní admini..
Avatar
Odpovídá na Marian Benčat
Jakub Šárník:29.1.2016 11:50

Ne jde o to, že jsem ještě nepsal "profesionálně" tak velkej projekt. Ale teď už jo a testy psát budu.

 
Odpovědět
29.1.2016 11:50
Avatar
Odpovídá na Jakub Šárník
Marian Benčat:29.1.2016 11:52

Proč to berete takto? Já vám řekl, že vám do článku dám argumenty, proč některé věci dělat jinakck čemuz se můžete vyjádřit a pak se sám svobodně rozhodnout. A vy na to reagujete sarkasmem :)

Odpovědět
29.1.2016 11:52
Totalitní admini..
Avatar
Odpovídá na Marian Benčat
Jakub Šárník:29.1.2016 11:53

Sarkasmem? Nebyl to sarkasmus

 
Odpovědět
29.1.2016 11:53
Avatar
Odpovídá na Marian Benčat
Jakub Šárník:29.1.2016 11:55

Vysvětlení: zatím jsem prostě nepsal velkej projekt, jen jsem s webovým vývojem experimentoval. Teď už ale jsem členem týmu a na něčem děláme, ale čeká nás přepsání a já sám prosazuji test driven development.

 
Odpovědět
29.1.2016 11:55
Avatar
Jaro
Člen
Avatar
Odpovídá na Jakub Šárník
Jaro:29.1.2016 12:56

Test driven development je výborná voľba, bude sa vám žiť omnoho ľahšie :)

Odpovědět
29.1.2016 12:56
“What would you do if you were 100% sure you couldn’t fail?”
Avatar
Petr Štěpánek:12.3.2017 14:00

Ahoj, není ta logika umístěná v Controleru místo v Modelu? Jak píšete mimo jiné, Controler dále zavolá další metodu modelu, která např. vypočítá věk. Díky

 
Odpovědět
12.3.2017 14:00
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Štěpánek
David Čápka:12.3.2017 14:02

A kde něco takového píšeme? Že někdo něco zavolá přeci neznamená, že to obsahuje.

Odpovědět
12.3.2017 14:02
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 21 zpráv z 21.