Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
Avatar
Drahomír Hanák:29.9.2012 10:33

Zdravím,
dělám teď podstatně větší aplikaci v PHP a to v týmu několika programátorů. Narazil jsem na problém, jak zvládnout modely v aplikaci. V menších aplikacích jsem měl pro každou tabulku v databázi jeden model, kde každý model obsahoval příslušné metody pro práci s tabulkou, validaci dat, data samotná atd. Problém je, že je mnohdy potřeba komunikovat v rámci jednoho modelu s více tabulkami. Taky bude potřeba občas změnit uložiště dat modelu (např.: pro cachování, databázi, souborový systém) nebo model vůbec nebude reprezentovat tabulku v databázi příp. nějaké jiné externí uložiště.

Mějme například třídu User. Kdyby ta třída obsahovala vše (tzn. metody pro manipulaci s databází, data atd.), bylo by velmi těžké třeba změnit uložiště za běhu nebo spravovat v rámci jedné instance víc uživatel. Pro každou změnu bych nejspíš musel dědit od třídy User a tam změnit třeba jen jednu věc, což není dobré.

Někde jsem četl rozdělení modelu do několika vrstev. První vrstva byla jen Entita (DTO - např. User) obsahující data, gettery, settery, příp. nějaké metody pro manipulaci přímo s daty. Další vrstvou byl mapper (např.: UserDbMapper a další), který zajišťuje veškerou práci s externím uložištěm a vrací instance User. Abychom se pak nemuseli rozhodovat v controlleru, který mapper použijeme (může být víc mapperů pro jiná uložiště), tak existuje ještě vrstva DAO (UserRepository), která se rozhodne, jaký mapper použije a má stejné rozhraní jako daný mapper. Může pak vracet různé entity (třeba pro jedno ID vrátí entitu Kniha, pro jiné entitu Casopis) Samotný mapper (třeba UserDbMapper) má tato třída třeba například jen jako privátní proměnnou, kterou si mění. Tím odstíníme presenter od rozhodování, kam data uložit. Poslední vrstvou byl pak servis, který obaloval různé akce do metod (třeba loggout() atp.) tak, abychom nemuseli manipulovat přímo s proměnnými entity (User), ale mohli jsme v klidu zavolat jen tu danou metodu. Zároveň tak omezuje manipulaci s danou entitou.

Co si o tom myslíte vy? Máte s tím nějaké zkušenosti? Je lepší to prostě naplácat do jednoho modelu, nebo to takhle rozdělit (přičemž některé vrstvy by šly u určitých modelů spojit)?

Pokud jsem něco nepopsal úplně správně, prosím opravte mě. Děkuji předem za všechny reakce.

Editováno 29.9.2012 10:37
 
Odpovědět
29.9.2012 10:33
Avatar
Kit
Tvůrce
Avatar
Odpovídá na Drahomír Hanák
Kit:29.9.2012 10:50

Model by měl být jen jeden. Můžeš ho však dědit a přidat mu vždy tu část datové struktury, kterou potřebuješ navíc. Správa více uživatelů v jedné instanci by neměla být problém, dokud nepoužiješ ORM. Pak už to problém je.

Volbu úložiště provádím ve chvíli, kdy vytvářím instanci modelu, resp. konstruktoru modelu předám jako parametr handler otevřeného databázového objektu.

Nahoru Odpovědět
29.9.2012 10:50
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Odpovídá na Kit
Drahomír Hanák:29.9.2012 11:00

V tomhle případě model bude jen jeden. On vlastně komunikuje jen se servisem, který mu vrací nějaké další instance tříd. V podstatě je to ale jeden model, jen rozdělený do víc vrstev.

Mně přijde totiž dost nelogické pracovat např. ve třídě User s více uživateli nebo že by třída Users reprezentovala jednoho uživatele, to taky nevidím jako dobrý nápad. Navíc ne vždy budu chtít vrátit ten samý DTO. Pro některé to bude třeba Kniha, pro další úplně jiná entita.

Editováno 29.9.2012 11:00
 
Nahoru Odpovědět
29.9.2012 11:00
Avatar
David Hartinger
Vlastník
Avatar
Odpovídá na Drahomír Hanák
David Hartinger:29.9.2012 13:10

V Rails jsem toto řešil tak, že User obsahoval instanční metody pro práci s jedním uživatelem a třídní metody pro práci s více uživateli. V .NET se tento způsob často používá. Na devbooku nemáme ORM, mám tam tedy UserManager, do kterého cpu obojí instančně :)

Nahoru Odpovědět
29.9.2012 13:10
New kid back on the block with a R.I.P
Avatar
Odpovídá na David Hartinger
Drahomír Hanák:29.9.2012 14:03

Stále mi tam nějak nesedí, že je to všechno v objektu User. User by měl reprezentovat jen jednoho uživatele. Takhle to porušuje Single Responsibility Principe (http://www.zdrojak.cz/…ncipy-solid/ - dál jen SRP). Na Rails jsem teď slyšel kritiku kvůli tomu, že neobsahuje DI, ale to je na jiné povídání.

Ten UserManager se mi ale celkem líbí. To máš jako takové spojení některých těch vrstev, co popisuji výše, ne? Resp. komunikuješ s uložištěm (databází), navenek nabízíš jasně dané rozhraní a vracíš instance User. Problém by ale nastal, kdybych chtěl třeba změnit uložiště za něco jiného než je databáze v průběhu aplikace (třeba Cache). To pak nemůžu jednoduše předat jinou instanci. Musel bych to v UserManageru zjišťovat a to je pak zodpovědný jak za manipulaci s uživateli, tak i za zjišťování, odkud je má brát, což porušuje SRP. Je jasné, že pro uživatele bych to asi nepotřeboval, ber to spíš obecně.

 
Nahoru Odpovědět
29.9.2012 14:03
Avatar
David Hartinger
Vlastník
Avatar
Odpovídá na Drahomír Hanák
David Hartinger:29.9.2012 14:14

Ono strašně záleží na názoru, Rails třeba míchá GET a POST do jednoho pole. Je to špatně? Je to dobře? .NET je zas plný statiky a funguje to perfektně. Já myslím, že ten návrh je sice velmi důležitý, ale také velmi přeceňovaný.

Na devbooku nemáme ORM, nevracím tedy žádné instance, místo toho vracím jen pole s nějakými daty, co funkce vytahá z DB, případně ještě nějak předpracuje.

Osobně bych hodil statiku na třídu User, tak by dávalo smysl, že jsou metody pro více uživatelů a zároveň by vše bylo na třídě User. Záleží na tom, kolik těch metod je. Ty se asi ptáš na to, kam to rozšiřovat dál, to také nevím, kdyby to bylo dlouhé, založil bych si ještě UserManager.

Já v tomhle hlavně nejsem moc dobrý a ani neuznávám příliš komplikované vzory, beru vše s rezervou, protože jinak by se z toho člověk zbláznil :) Občas, když vidím cizí kód, tak si říkám... však to znáš :D

Nahoru Odpovědět
29.9.2012 14:14
New kid back on the block with a R.I.P
Avatar
Odpovídá na David Hartinger
Drahomír Hanák:29.9.2012 15:27

Máš pravdu, asi to moc řeším, jen si nechci uprostřed vývoje uvědomit, že to dělám špatně a že to takhle nebude fungovat. Pro každý controller mám jeden model, takže snad bude stačit použít Nette\Database v jeho metodách pro úpravu databáze. ORM se mi zdá pro moje účely dost zdlouhavé. Máte s jeho implementací zkušenosti (třeba Doctrine 2) nebo bych ORM používat neměl?

Každopádně díky za vaše názory, dost mi to pomohlo ;)

 
Nahoru Odpovědět
29.9.2012 15:27
Avatar
David Hartinger
Vlastník
Avatar
Odpovídá na Drahomír Hanák
David Hartinger:29.9.2012 15:40

Já mám k ORM neutrální vztah, když ho někde dostanu odladěné, použiji ho, když ne, vyhnu se mu. Co jsem slyšel, tak Doctrine není úplně nejjednodušší, ale nikdy jsem s tím nedělal.

Jestli bys měl nebo neměl používat ORM je otázka pro tebe, je to prostě jeden z přístupů, který má své výhody a nevýhody. ORM se běžně používá v hotových řešeních (Rails, ASP), ale víme, že PHP nic takového nenabízí a naopak existují mraky různých řešení třetí strany. Proto já v PHP ORM nepoužívám, ale třeba bych byl překvapen, že funguje dobře. Dokud budeme pracovat s relační databází, bude to stále kontroverzní téma.

Nahoru Odpovědět
29.9.2012 15:40
New kid back on the block with a R.I.P
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 8 zpráv z 8.