5 vrstvovy model – facade VS repository
- marioff
- Člen | 69
Ahojte, 2 dni studujem 5 vrstvovy model (Entity, Facade, Service, Repositary, Entity Manager).
Mam rozpisanu aplikaciu od ineho programatora s vyuzitim Kdyby\Doctrine a strasne ma pletie ako je to oddelene tam.. mam s toho strasny hokej, a neviem ci tomu nerozumiem alebo je to naprogramovane nespravne..
napr. fasada a vnej podobne metody:
public function findByCategory(Category $category)
{
$qb = $this->dao->createQueryBuilder('article')
->addOrderBy('article.created', 'DESC');
$categories = $this->dao->related('categories')->children($category);
$categories[] = $category;
$criteria['article.categories.id'] = $categories;
$qb->whereCriteria($criteria);
return new ResultSet($qb->getQuery());
}
nemalo by to byt v nejakom repozitari a vo fasade si len zavolat repozitar->findByCategory ? Chapem to spravne?
- Filip Procházka
- Moderator | 4668
Repozitáře bys právě dědit neměl, jenom proto abys do nich přidával další metody. Takové metody patří buď do services/facades nebo do QueryObjectů, ale né do repozitářů :)
- marioff
- Člen | 69
cize na repozitare sa vykaslat a rovno takto vyberat data priamo vo fasade (resp. v services) .. neviem, mam v tom uz riadny hokej, podla tych clankov som to pochopil tak ze najlepsie je vybrat data vo fasade napr.:
public function bestArticlesInCategory(Category $category)
{
$articles = $this->repository->findByCategory($category);
tu este nieco urobim s $articles...
return $articles;
}
a ked budem potrebovat v inej fasade ziskat $articles tak znova skopirujem tu metodu do dalsej fasady? alebo ju urobit v services a odtial to potom len tahat vo fasadach?
lebo som bol v tom, ze toto riesi repozitar :(
- Šaman
- Člen | 2666
Repozitář + DAO by ti měly tvořit nízkoúrovňovou vrstvu dotazů do db. Všechno by mělo komunikovat s databází přes ně (nebo přes něj, já to mám taky dohromady v jedné třídě).
Fasáda je pro vyšší logiku, právě třeba
bestArticlesInCategory
. V této metodě bys měl ale pracovat jen
s metodami repozitářů ArticleRepository
, případně i
CategoryRepository
, ale už ne se samotnou db. V Doctrine
nedělám, takže ti neporadím konkrétní efektivní postup. V jednoduchém
modelu bych to udělal asi takto:
<?php
public function bestArticlesInCategory(int $categoryId, int $count = 1)
{
$articles = $this->articleRepository->findByCategory($categoryId)->orderBy('rating DESC')->limit($count);
// případně si vrátíš všechny articles v dané kategorii a nějak je tu teď profiltruješ
return $articles;
}
?>
P.S. Je dobré mít konzistentní názvy metod. Aby bylo jasné, co ti
bestArticlesInCategory
vrátí. Já bych si v ukázce třeba
nechal vracet NDb\Selection
a pak by se metoda jmenovala
findBestArticlesInCategory
, ale pokud by se celá fasáda jmenovala
třeba ArticleService
, pak bych to zkrátil na
findBestInCategory
, že hledám články je jasné z použité
fasády. Pokud chci vracet jediný záznam (NDb\Row), pak by metoda začínala
na get. Je na tobě, jakou konvenci si zavedeš, ale pak ji dodržuj.
P.P.S. Asi jsem nezdůraznil jednu věc – fasáda nikdy nedědí
repozitáře, ta obaluje repozitáře! Používá kompozici (skládání),
nikoliv dědičnost.
Tedy si je nechá injectnout.
Editoval Šaman (29. 12. 2014 18:15)
- marioff
- Člen | 69
ano, presne takto nejak som to myslel, ze repozitar bude zabezpecovat data pre fasadu a ta ich bud vrati presenteru, alebo ich este nejak doplni ci prefiltruje (napr. pomocou services).. len momentalne je aplikacia v takom stave, ze data sa vyberaju rovno vo fasade ako je v prvom prispevku.. preto som nevedel ci je to OK, alebo som nepochopil ten 5 vrstvovy model..
- Šaman
- Člen | 2666
No, tady asi budeš potřebovat zkušeného Doctrináře, protože ty v té první ukázce nepracuješ přímo s databází (to by bylo špatně), ale ani nevyužíváš konkrétní repozitář/DAO, jak popisuje 5ti vrstvý model. Ty využíváš queryBuilder nad nějak doctrinou automaticky vytvořeném dao, takže ten první příspěvek IMHO 5ti vrstvý model obchází a používá jiné principy.
To, co jsem popisoval já, je obecný model, v Doctrine můžeš jednu věc udělat několika způsoby (a ten co používáš ty, je nejjednodušší – jen asi schovává to rozvrstvení).
- marioff
- Člen | 69
jj islo mi o to, ze sa vo fasade vyberaju data (uz nehra ulohu ci cez DAO – ja som to neprogramoval, skor sa natom ucim nette) , len mi to strasne nesedelo, uz len pri pomysleni, co ak budem podobne data potrebovat aj inde.. ale pochopili sme sa, dakujem za navedenie spravnym smerom
- Šaman
- Člen | 2666
Jo, rozumíme si. To je přesně ten problém tohoto, nejjednoduššího řešení. Taky jsem na to už narazil v několika projektech, proto zatím Doctrinu nemám moc rád. Asi to jde udělat u dobře, ale taky dovolí prasit.
Problém nastane ve chvíli, kdy budeš chtít na úrovni repozitáře omezit
třeba, že za platné članky se považují jen takové, které mají
$deleted = FALSE
. Ve tvém projektu to znamená projít všechny
repozitáře a fasády (a raději i presentery) a dopsat to ručně. Pokud za
přístup ke článkům zodpovídá jen ArticleRepository, tak projdeš jen ten.
A v mém řešení se doplní jen metoda
$articleRepository->findAll()
, která vrací Selection, nad
kterém probíhají všechny další operace s články.
- Filip Procházka
- Moderator | 4668
FYI @Šaman, v doctrine existuje koncept filtrů, který umí automaticky do
vytvářených dotazů přidávat podmínky. Napíšu tedy třeba
SoftDeleteFilter, který mi vygeneruje něco jako
AND e.deleted = FALSE
a to se přidá jako podmínka do všech
dotazů co vytahují data. Navíc si to můžu na frontendu zapnout a
v administraci vypnout podle potřeby, takže se pak ke smazaným snadno
dostanu :)
A prasit se dá ve všem :)
- Prado
- Člen | 21
Já se fasádu snažím používat jako rozhraní mezi modelem a kontrolerem. Je to pro mě něco, odkud když odříznu model a reimplementuji ho, tak to zbytek aplikace nepozná. Z lenosti mám fasádu většinou jednu, ale v zásadě jich může být více podle řešené domény. Sice to nikdy nedodržím, ale v zásadě mám takovou představu, že obsahuje jen minimum logiky a spíše to ověří práva, ohlídají transakci a volají metody ze servisy nebo repository. Fasáda u mne vrací entity, takže z kontroleru mohu volat i jejich metody, ale chápal bych i transformaci do DTO.
Servisní třídy u mne zahrnují vyšší logiku. Injektují se do nich repository i jiné servisy, podle potřeby. Od fasády se u mne liší tím, že neřeší transakce nebo práva: co dostanou za úkol, to provedou nebo vyhodí výjimku.
Někdy mám ještě utility. To jsou zcela bezstavové třídy, takové knihovny funkcí. Obvykle zjistím, že to samé již někdo naimplementoval lépe a měl jsem využít jeho práci.
Repository pro mne vrací datové kolekce podle kritérií, takže výběr článků podle kategorie bych dal rozhodně tam. Nebo pokud používáte query objekt do servisní vrstvy.