Architektura aplikace

28. listopadu 2017

Poznámky rozšiřují informace z článku https://www.zdrojak.cz/…-doctrine-2/. Dobrý souhrn poskytuje i https://fprochazka.github.io/…rchitecture/.


ondrejmirtes [Jan 12th at 1:52 PM]
Tak třeba:

public function mrdSending()
{
    if (!$this->getStatus()->equals(MailingCampaignStatus::IN_PROGRESS())) {
        throw new \Slevomat\Mailing\Campaign\MailingCampaignInvalidStatusChangeException($this->getId(), $this->getStatus(), MailingCampaignStatus::SENDING());
    }

    $this->setStatus(MailingCampaignStatus::SENDING());
}

ondrejmirtes [Jan 12th at 1:53 PM]
setStatus není public, takže si s tím nemůže dělat každý, co chce


ondrejmirtes [Jan 12th at 2:21 PM]
Jde o to, že je potřeba mít podchycené operace s entitou. Setter co nic nezvaliduje, je defacto public property. A ty by už nikdo z vás asi nepsal :)


ondrejmirtes [Jan 12th at 2:22 PM]
nebo třeba když máte datum od a do, tak nedává smysl na to mít dva oddělené settery. Musíte napsat jeden, co to zvaliduje


ondrejmirtes [Jan 12th at 2:30 PM]
nejradši mám, když jsou argumenty metod, konstruktorů apod. všechny povinné, a píšu na různé usecasy další metody/třídy


ondrejmirtes [Jan 12th at 2:40 PM]
@jelen07 Tohle je dostatečně obecný princip, který se dá aplikovat všude. Podle mě všude oceníš to, že si s tvými objekty nejde dělat cokoli, ale pouze předurčené věci. Pokud se v systému vyskytuje něco, s čím má jít obecně jakkoli manipulovat, dá se tam udělat výjimka, ale pouze na tom místě a nikoli všude.


ondrejmirtes [Jan 12th at 2:41 PM]
Jde prostě o OOP, u kterého si nevytrháš vlasy.


ondrejmirtes [Jan 12th at 3:01 PM]
a kdo ti zajišťuje, že entita, potažmo DB row, bude obsahovat jen validní kombinaci dat?


brosland [Jan 18th at 12:20 PM]
Zdravím, tí čo používate 5-vrstvový model (alebo inak povedané fasády a služby) mám na vás otázku:
Podľa NetteCamp-2016-doctrine-model som usúdil, že by každá fasáda, ktorá pracuje s určitým typom entity mala mať metódu create*(array $values), ktorá pomocou služby danú entitu vytvorí.
Otázka: mala by existovať aj obdobná metóda pre update ⇒ update*(Entity $entity, array $values)?
Je to okay, ak priamo v triede formulára nastavím hodnoty do entity pomocou setterov?


ondrejmirtes [Jan 18th at 12:53 PM]
Fasáda by měla mít metody podle toho, co má dělat :slightly_smi­ling_face: Pokud fasáda vůbec entitu nevytváří, nedává smysl metoda create. Pokud jí neaktualizuje, nedává smysl metoda update :)


ondrejmirtes [Jan 18th at 12:53 PM]
Jinak k tomuhle tématu doporučuju https://www.zdrojak.cz/…-doctrine-2/


ondrejmirtes [Jan 19th at 7:55 AM]
@brosland: Podle mě bys do fasády neměl posílat array $values, ale každou hodnotu jako parametr metody zvlášť, to je daleko hezčí a udržitelnější API


pilec [Jan 19th at 9:09 AM]
btw osobně ještě při větším množství parametrů je ještě přebaluju do typového DTO


ondrejmirtes [Jan 19th at 9:30 AM]
Tady se neorientuju ani tak podle počtu parametrů, ale pokud třeba tři věci předávám furt společně, je čas na objekt :) většinou pak nad nimi provádím i stejné operace, které se do toho objektu dají dát


ondrejmirtes [Mar 10th at 10:11 AM]
Neměla by jít vytvořit nevalidní entita. Tzn. všechny povinné argumenty předat konstruktoru. Dále pak taky – žádné settery!
Entita by měla mít usecasové metody na všechno, co s ní má jít dělat. (http://www.yegor256.com/…re-evil.html). Což málokdy znamená setovat do ní úplně cokoli. Každý objekt by měl zodpovídat za svoji konzistenci, tzn. že i entita. Co se týká ji samotné, tedy jejího řádku v databázi a případně jejích asociací, by si měla ověřovat a kontrolovat sama. Jakmile ovšem je potřeba sáhnout do nějakého externího zdroje, nebo kvůli ověření napsat složitější SQL, už je validace záležitost servisy.


ondrejmirtes [Mar 10th at 10:12 AM]
Co se týče vícenásobné validace, tomu se asi nedá úplně vyhnout. Validace v modelu by měla být povinná – protože by model neměl jít rozbít. A záleží, kdo pak ten model používá – jestli webové formuláře, nebo API, nebo CLI. Pro každého takového uživatele je validace dost specifická. Třeba pro CLI lze další vrstvu validace úplně vynechat, protože tam nevadí, že vyletí výjimka z modelu až do outputu.


ondrejmirtes [Mar 10th at 10:13 AM]
Ono vlastně u validace záleží jen na tom, jak rychlý chcete dát feedback uživateli. Jestli nevadí, že selže odeslání formuláře a vykreslí se chyba při dalším načtení, stačí mít validaci v modelu a chytat výjimky. Pokud chcete rychlejší feedback, musíte implementovat validaci v JS…


oli [Mar 10th at 10:17 AM]
@ondrejmirtes díky. Mě se to takhle líbí a takhle bych to chtěl. No, neprosadil jsem to :slightly_smi­ling_face:
Hlavní problém, který máme je v konstruktoru. Pokud má entita 4 povinné parametry, tak jsou v konstruktoru a je to třeba jméno, příjmení, rc a email. Pokud bude spatne rc a email, tak jak oba tyto parametry vypises uzivateli naraz ze jsou rozbity? pokud vyhodis vyjimku, tak se k tomu dalsimu uz ani nedostanes. a v konstruktoru vlastne ani nevytvoris objekt…


ondrejmirtes [Mar 10th at 10:19 AM]
@oli Konzistence modelu a výpis chyb uživateli jsou úplně oddělené problémy, tak bych se k nim tak choval.


ondrejmirtes [Mar 10th at 10:28 AM]
Každý objekt zodpovídá za svou konzistenci a zapouzdřenost. Tímhle bych začal, třeba se ti pak povede přijít s nějakým lepším řešením, ale z tohohle bych neslevoval.


ondrejmirtes [Mar 10th at 10:32 AM]
K zamyšlení – u stringu ti typicky stačí povinnost/nepo­vinnost. Jakmile někde potřebuješ ověřovat konkrétní tvar – RČ – mělo by to být na zvláštní objekt. Když si pak v entitě říkáš o objekt RČ, už víš, že bude validní.


ondrejmirtes [Mar 10th at 10:37 AM]

$rc = new PersonalNumber('801111/0654'); // vyhodi vyjimku, pokud je spatne

public function __construct(PersonalNumber $rc) // uz vim, ze je validni
{

}

oli [Mar 10th at 10:39 AM]
jeste, kdo by mel validovat jestli je string mezi min, max? Service?


ondrejmirtes [Mar 10th at 10:40 AM]
entita


ondrejmirtes [Mar 10th at 10:40 AM]
pokud ale potrebujes treba overit, ze je DIC v databazi unikatni, tak uz to entita sama nezvladne, musi servisa


oli [Mar 10th at 10:40 AM]
a pokud je to v konstruktoru?


oli [Mar 10th at 10:40 AM]
tak to pri jmene, ze je kratke spadne, ale ze je spatne prijeni uz nezjistim


ondrejmirtes [Mar 10th at 10:45 AM]
pokud mas overovani v entite, tak v servise to znovu neni potreba… jen pokud chces tu chybu reportovat uzivateli, v typicke CRUD aplikaci, tak nastav formular tak, aby to overovani v entite nespadlo, tedy aby tam nepropluly spatny data


ondrejmirtes [Mar 10th at 10:45 AM]
v M (modelu) staci ta validace jednou, logicky


oli [Mar 10th at 10:46 AM]
to mam namysli. bral jsem to ze to je jedno jestli ve formulari nebo v service.


ondrejmirtes [Mar 10th at 10:47 AM]
servisa je soucasti modelu a v modelu uz to overeni mas, v entite


ondrejmirtes [Mar 10th at 10:51 AM]
kdyz si rekne o objekt RC, tak tam nedovoli predat neco spatne :slightly_smi­ling_face:


ondrejmirtes [Mar 10th at 10:51 AM]
premyslet je potreba hlavne pri navrhu rozhrani te entity… hezke na tom pak je to, ze ti nedovoli udelat chybu


ondrejmirtes [Mar 10th at 10:52 AM]
pokud tam predavas neco, co nemas, tak aplikace spadne, ale to nevadi, hlavne, ze se neposkodila data


ondrejmirtes [Mar 10th at 10:52 AM]
mrknes na chybu v logu a validaci si opravis, to je v pohode


ondrejmirtes [Mar 10th at 11:12 AM]
Ne, uživatel uvidí obecnou 500ku nebo flashku „něco se pokazilo“.


ondrejmirtes [Mar 10th at 11:12 AM]
Text výjimky do rukou uživatele nikdy :skull:


ondrejmirtes [Mar 10th at 11:12 AM]
Chyba přijde do logu, kde si ji vývojář přečte a nekonzistenci opraví. Reálně se to zas tak často neděje.


ondrejmirtes [Jun 14th at 2:24 PM]
Důležité prvky architektury:

  1. Controller – přijme request, zvaliduje správné typy a hned ho pošle do fasády. Datetime jako value objekty, identifikátory entit jako skaláry. Dále handluje výjimky vyhozené fasádou.
  2. Fasáda – přijme parametry z controlleru, shání entity přes repository, volá servisy s business logikou, persistuje a flushuje, stará se o DB transakce.
  3. Servisy – obsahují unit testovatelnou business logiku s hezkým inputem a outputem. Tohle je volitelná část architektury, pokud nechceš testovat, můžeš ten kód mít přímo ve fasádě. Ale osobně mi to přijde dost důležité.
  4. Repository – shání data v databázi a dalších úložištích (filesystém, redis atd.).
  5. Entity – reprezentuje řádek v databázi, zodpovídá za jeho konzistenci a případně za konzistenci navázaných asociací. Nesmí být rozbitelná, tedy povinné parametry v konstruktoru + ne settery, ale usecasové metody alá approve, publish, reject atd. Vyhodí výjimku, pokud se provádí nepovolená operace nad jejím stavem.

ondrejmirtes [Jun 14th at 7:43 PM]
@ondravotava Záleží, co s tou entitou děláš. Neumožňoval bych s ni navenek dělal cokoli, protože ne všechno vždy jde dělat. Některé hodnoty taky souvisí spolu. Pokud máš třeba v entitě pole dateFrom a dateTo, tak bych na to napsal jeden setter setDates($from, $to) a zkontroloval bych, že $from < $to. Různé stavy a workflow entity bych nedělal přes public setStatus, ale přes use-casové metody jako approve, reject, které uvnitř kontrolují, že to, co chceš, zrovna jde udělat… Spousta metod taky třeba kromě aktualizace nějaké hodnoty chce zároveň měnit vnitřní modifiedTime, tře­ba…

Líbí se vám tato stránka?