Do databáze jen z modelu?

netrunner
Člen | 7
+
0
-

Ahoj,
snažím se rozvrhnout strukturu starší aplikace, kterou bych rád přepsal do MVP s nette. Vcelku je mi jasný princip MVP, všechny jednoduché ukázky včetně quickstartu jsou jasné. Jenže praxe taková není.

Aplikace je velmi rozsáhlá, řekněme vyšší desítky až stovky presenterů. S modelem není problém u věcí typu UserManager, TohleRepository a tak. Jenže pak bude taky ve spoustě presenterů hromada „jednorázových“ akcí. Třeba vytáhnout data ze spojení 4 tabulek. Některé tabulky se jinde v aplikaci používat budou, určitě ale ne v tomto spojení. Nebo naopak na základě uživatelského požadavku upravit jednu hodnotu v databázi, nikde jinde se taková úprava dít nebude, nemá to nic společného s nějakým jiným logickým celkem.

A teď mám před sebou dvě cesty:

  1. Jednoduše si do databáze sáhnout přímo z presenteru. Sice tím trochu poruším princip mvp, ale je to průhledné a rychlé.
  2. Snažit se to všechno silou nacpat do modelu. Tím by se mi buď zaplevelovaly třídy v modelu ($users->getUsersJoinToGroupsJoinToLevelsJoinToBlabla()), nebo by vznikaly „modely pro modely“, tedy k SomePresenter by vznikla třída SomeModel s metodami o jednom dotazu do databáze. To by bylo zase extrémně formalistické a nepřehledné.

Co je podle vás lepší?

chemix
Nette Core | 1294
+
-3
-

@netrunner Nepremyslel jsi o ORM mozna by to dost veci ulehcilo ve tvem pripade podle toho co pises. Spousta veci bys resil pomoci orm primo v presenteru a mel to pekne obalene. A pokud bys potreboval neco komplikovanejsiho tak bys pouzil nejakou fasadu, servicu co by bylo treba pro lepsi citelnost a znovu pouzitelnost

netrunner
Člen | 7
+
0
-

Přemýšlel, ale v tomhle případě by mi to moc nepomohlo. Přes libovolné ORM se dost komplikuje spojování tabulek, respektive je potřeba spoustu psaní kvůli jednorázové akci. Navíc jsou to ty „modely pro modely“, kdy z jednoho jednoduchého query() udělám několikatřídovou obludu.

Zkusím to rozepsat trochu konkrétněji. Mám třeba tabulky:

users (id, name)
property (id, name)
property_users (property_id, user_id)
clubs (id, name)
clubs_users (club_id, user_id) --unique

A potom někde v jednom presenteru potřebuji vypsat všechen property členů určitého clubs. Ano, takto zjednodušené to svádí k něčemu jako:

$list = $propertyManager->getByClubMember($clubId);

Jenže když je těch spojení více, začne v tom být zmatek. Patří to do PropertyManageru? Nebo do ClubManageru? Libovolný Manager se mi zaplevelí jednou metodou navíc, která není znovupoužitelná. A když mám takových věcí v aplikaci více, tak by mi ty Managery nabobtnaly do neuvěřitelných rozměrů. Navíc by se mi mohlo stát, že místo jednoho query se spojením tabulek jich půjde do databáze postupně deset, což by se rozhodně projevilo na výkonu.

Jak nad tím přemýšlím, tak mi jde asi spíš o akademickou debatu, zda je špatně se na DB jako takovou dívat jako na součást modelu. Co jde a má smysl vyčlenit do vlastní třídy, co smysl nedává, tak prostě použít přímo DB (alias DataManager). Na věci jako „jenže takhle nebudeš moci v budoucnu vyměnit databázi“ v tomhle případě nevěřím. Požadovaná databáze je prostě součástí aplikace, pro jinou databázi by se stejně musely dotazy ve specifických věcech přepsat.

MajklNajt
Člen | 470
+
+6
-

Ak chceš akademickú debatu, potom si treba uvedomiť, že podstata MVC je v tom, že presenter nezaujíma, odkiaľ sa dáta berú, on si ich jednoducho vypýta od modelu a odovzdá ich šablóne. Všetká biznis logika, výpočty a stav aplikácie patria do modelu, presenter už nezaujíma, či sú dáta uložené v cache, v databáze alebo v súboroch…

Šaman
Člen | 2630
+
+2
-

Čistě akademicky DB vrstva patří do modelu, jen je méně zapouzdřený/hluboký než když nad ní vybuduješ ještě vrstvu repository a fasády pro získávání už předžvejkaných dat.

Dokonce v budoucnu budeš moci vyměnit databázi, i když prakticky to půjde jen v rámci relačních databází a obecné sql syntaxe. Přepíšeš jen string v configu, který říká jaký driver se má použít.

Kolik vrstev modelu použiješ je jen na tobě. Každá přináší nějakou další abstrakci a znovupoužitelnost, ale také zvyšuje komplexitu aplikace.


A zcela neakademicky musím napsat, že správná volba nemusí vést nutně k akademicky čisté aplikaci. Jde o to odhadnou poměr pracnost/učitečnost. Jestli se ten projekt bude dál rozvíjet, má cenu ho refaktorovat víc do hloubky, než pokud jde jen o to zařídit aby byl spustitelný na php7 serveru, ale jinak se na něj už sahat nebude.

Editoval Šaman (29. 3. 2020 14:50)

F.Vesely
Člen | 367
+
+1
-

Dalsi veci je testovani. Otestovat Repository, zda ti vratila spravna data je urcite mnohem jednodussi nez testovat Presenter.

GEpic
Člen | 562
+
+2
-

Za mě je asi lepší to, co ti víc vyhovuje (a časem to pilovat). Nesnažit se zkrátka aplikovat filozofii někoho jiného, která ti nebude sympatická. Pořád je to tvůrčí proces a každý k tomu může přistupovat jinak. Čím víc toho ale píšu, tím víc se toho snažím dostat z Presenterů pryč. Ono totiž u spousty věcí jsem si taky řekl, že tohle se použije jenom tady. No a pak za rok se to má použít najednou jinde. A co teď? Duplikovat kód, a nedejbože v tom potřebovat udělat drobnou změnu? Tak se snažím vše rozdělovat i třeba do menších celků, ale zamyslím se ještě před samotnou implementací nad tím, zda-li by to nešlo pojmout obecně. V longrunu se tohle hodně vyplácí. Protože kde to oj*beš dnes, za týden / měsíc / rok se ti může mnohonásobně vymstít.

Editoval GEpic (29. 3. 2020 23:24)

m.brecher
Generous Backer | 707
+
0
-

Model v návrhovém vzoru MVC chápu tak, že modelové třídy a databáze dohromady reprezentují jak obchodní logiku tak obchodní data uživatele aplikace. Modelové třídy zapouzdřují relativně nepohodlnou práci s daty přes surové SQL a zpřehledňují kód aplikace. Třídy Nette\Database\Table představují už určité zapouzdření, takže je lze považovat za takový hodně jednoduchý model. Kód v presenteru:


$this->template->article = $this->articleRepository->getArticle($id);

je v podstatě stejně přehledný jako:


$this->template->article = $this->database->table('article')->get($id);

Ušetří se vytváření modelové třídy ArticleRepository, předání do presenteru přes konstruktor a registrace služby v common.neon.

Já bych s klidným svědomím nějaké velké množství modelových pidi-tříd, které by měly obsahovat pouze získání jednoho řádku, nebo výběru z tabulky psal v presenteru pomocí Nette\Database\Table. Pokud by získávání dat bylo o něco málo složitější, asi by bylo lepší psát modelové třídy.

Editoval m.brecher (19. 9. 2021 23:27)

Kamil Valenta
Člen | 748
+
+5
-

m.brecher napsal(a):

Ušetří se vytváření modelové třídy ArticleRepository, předání do presenteru přes konstruktor a registrace služby v common.neon.

Tak jednoduché to není. Zrovna „článek“ je dobrý příklad. Edituje se málokdy, zobrazuje se tisíckrát za den. Pak se člověk rozhodne, že by ho chtěl cachovat. Zatímco v modelu mám možnost z getArticle($id) udělat cache-wrapper, pokud si presentery (nebo desítky presenterů) a komponenty (nebo desítky komponent) sahají přímo na database->table(‚article‘)->get($id);, musím to všude najít a všude upravit. A při každé změně zas.

Šaman
Člen | 2630
+
+1
-

Přesně. Já měl zase požadavek na logování změn v db. Takže pokud ten článek někdo zedituje, nebo smaže, vytvořím o tom záznam v tabulce log. Což je extrémně jednoduché, pokud mám ve třídě Article metody edit() a delete() a všechny presentery je používají.

m.brecher
Generous Backer | 707
+
0
-

OK, souhlasím, že MVC je prověřený návrhový vzor a dlouhodobě se vyplatí trocha práce navíc, a je pravda, že kdykoliv jsem se pokusil si nějak ulehčit život, v 80% se to dlouhodobě nevyplatilo :)