Oznámení
LeanMapper – kompletní modelová vrstva a transakce
před 6 lety
- Filip111
- Člen | 244
Trochu se trápím s rozvrstvením modelové vrstvy, tak by mě zajímalo, jak to řešíte vy.
Jednoduchý příklad – odešlu formulář a následně z jeho hodnot vytvořím entitu User a jeho role (m:hasMany) UserRole.
Ideláně si to představuji tak, že vytvořím entitu User, do vazby jí
nastavím role UserRole a předám celý vytvořený pavouk entit nějaké
fasádě, která to celé uloží. I za cenu, že bude muset použít více
repository.
Jenže to v LM nejde, protože dokud není entita uložená, nemůžu ji spojit
vazbou s jinou entitou. (nevím přesně chybu, něco s detached entity…)
(v Doctrine2 by to šlo, tam se entita dokáže persistovat jestli se
nepletu)
Jak a kde tedy vše uložit?
Požívám nad repository ještě fasády, ale zjišťuji, že asi ne úplně ideálně. Obvykle vytvořím entitu už v presenteru na základě dat z formuláře, předám ji fasádě, ta ji přes repository uloží a provede další navazující akce (pošle maily, něco zaloguje apod.). Pro práci s jednou entitou s tím nemám problém.
Jenže v případě ukládání více provázaných entit, narážím
právě na výše zmíněný problém. Řeším to tak, že v presenteru
vytvořím několik entit a každou přes svojí fasádu/repository uložím a
průběžně jak je ukládám je spojuji dohromady.
Výsledkem jsou bobtnající presentery a hloupé fasády.
Co s tím?
Pokud budu vždy do fasády předávat data z formuláře, tak se mi ztrácí
znovupoužitelnost konkrétní metody ve fasádě a i když přesunu spoustu
kódu do fasády nic dalšího mi to nepřinese.
Naopak pokud budu fasádě předávat jednu entitu, budu mít pořád veškerou
logiku v presenteru, což je taky špatně.
Související otázka – jak a v jaké vrstvě řešíte
transakce?
Opět bych zase vidět ideální způsob předat celý pavouk Entit nějaké
fasádě, která v rámci jedné transakce vše uloží. Jenže to nejde.
Díky, Filip
ps.: u malé jednoúčelové aplikace je to celkem jedno, ale jak se rozrůstá začíná mě to pálit stále víc
Editoval Filip111 (31. 12. 2013 9:27)
před 6 lety
- Tharos
- Člen | 1042
Ahoj,
jak by se Ti líbilo cca takovéto řešení Tebou uvedeného příkladu?
/**
* Simple proxy to transactions
*/
class Transaction
{
/** @var LeanMapper\Connection */
private $connection;
/**
* @param Connection $connection
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* @param string|null $savepoint
*/
public function begin($savepoint = null)
{
$this->connection->begin($savepoint);
}
/**
* @param string|null $savepoint
*/
public function commit($savepoint = null)
{
$this->connection->commit($savepoint);
}
/**
* @param string|null $savepoint
*/
public function rollback($savepoint = null)
{
$this->connection->rollback($savepoint);
}
}
class Users
{
/** @var Transaction */
private $transaction;
/** @var UserRepository */
private $userRepository;
/** @var UserRoleRepository */
private $userRoleRepository;
/**
* @param Transaction $transaction
* @param UserRepository $userRepository
* @param UserRoleRepository $userRoleRepository
*/
public function __construct(Transaction $transaction, UserRepository $userRepository, UserRoleRepository $userRoleRepository)
{
$this->transaction = $transaction;
$this->userRepository = $userRepository;
$this->userRoleRepository = $userRoleRepository;
}
/**
* @param User $user
* @param UserRole[] $userRoles
*/
public function createUser(User $user, array $userRoles = array())
{
$this->transaction->begin();
try {
foreach ($userRoles as $userRole) {
$this->userRoleRepository->persist($userRole);
}
$user->addToRoles($userRoles);
$this->userRepository->persist($user);
$this->transaction->commit();
} catch (\Exception $e) {
$this->transaction->rollback();
throw $e;
}
}
}
Editoval Tharos (31. 12. 2013 10:55)
před 6 lety
- Filip111
- Člen | 244
Třída Transaction se mi líbí – nechtěl jsem používat v presenteru
connection, takže tohle je asi ok.
Nemusim mít výčitky, když to použiju v repository nebo i presenteru,
protože už je to zapouzdřený.
Třída User je přesně to nad čím si lámu hlavu, jestli používat či nikoliv. Dokud jsem nepoužíval ORM tak jsem podobný objekty používal. S tím jak jsem ale začal používat ORM, tak jsem začal používat entity.
S čím pak pracuješ např. v presenteru/šabloně? S entitou User nebo
s objektem User, který jsi uvedl?
(nebo je entita User a objekt User to samé, jen jsi ořezal deklarace
properties?)
Jakou zodpovědnost by měla mít třída User? Jen uložení celého pavouka
entit souvisejících s uživatelem nebo i jejich čtení a
předávání dál?
Občas mi tam právě takhle mezivrstva chybí, ale nevim jestli to
zapouzdření nebude už moc přehnaný.
Entita, Repository, Objekt (např. User), Fasáda (pracující někdy s entitou
někdy s objektem User) (?)
Vrstvu s fasádou bych chtěl ponechat, protože mi přijde ideální právě na nějaký činnosti, které už nemusí být v transakci a které nějak souvisí s např. vytvořením uživatele. Tedy právě zaslání mailů, případně práci napříč dalšími službami.
Ty to takhle používáš nebo jsi jen vytvořil nějaký příklad?
Díky
před 6 lety
- Pavel Macháň
- Člen | 285
Filip111 napsal(a):
Třída Transaction se mi líbí – nechtěl jsem používat v presenteru connection, takže tohle je asi ok.
Nemusim mít výčitky, když to použiju v repository nebo i presenteru, protože už je to zapouzdřený.Třída User je přesně to nad čím si lámu hlavu, jestli používat či nikoliv. Dokud jsem nepoužíval ORM tak jsem podobný objekty používal. S tím jak jsem ale začal používat ORM, tak jsem začal používat entity.
S čím pak pracuješ např. v presenteru/šabloně? S entitou User nebo s objektem User, který jsi uvedl?
(nebo je entita User a objekt User to samé, jen jsi ořezal deklarace properties?)
Jakou zodpovědnost by měla mít třída User? Jen uložení celého pavouka entit souvisejících s uživatelem nebo i jejich čtení a předávání dál?Občas mi tam právě takhle mezivrstva chybí, ale nevim jestli to zapouzdření nebude už moc přehnaný.
Entita, Repository, Objekt (např. User), Fasáda (pracující někdy s entitou někdy s objektem User) (?)Vrstvu s fasádou bych chtěl ponechat, protože mi přijde ideální právě na nějaký činnosti, které už nemusí být v transakci a které nějak souvisí s např. vytvořením uživatele. Tedy právě zaslání mailů, případně práci napříč dalšími službami.
Ty to takhle používáš nebo jsi jen vytvořil nějaký příklad?
Díky
Já osobně bych vnitřek třídy Users měl přímo v UserFacade. Nevidím důvod to mít takhle mimo, přeci jen od toho je tu ta fasada aby to cele obalila a v prezenteru si po nějaké akci vyvolal createUser($name, $username,…) a dostal si treba jen true|false jako odpověď pro prezenter.
před 6 lety
- Filip111
- Člen | 244
@EIFEL:
Tzn. ty bys třídu User viděl jen jako pomocnou pro uložení Usera se všemi
vazbami s souvisejícími entitami? Ve zbytku systému bys použil klasicky
entity/fasády?
To se mi moc nelíbí – když už třídu User zakládat, tak by měla mít širší využití než jen jako berlička pro založení uživatele. Pak by se struktura modelu ještě víc rozštěpila – pro založení bych používal třídu User, pro editaci/čtení už jen fasádu která pracuje s entitami.
před 6 lety
- Tharos
- Člen | 1042
@Filip111:
Třída Transaction se mi líbí – nechtěl jsem používat v presenteru connection, takže tohle je asi ok.
Nemusim mít výčitky, když to použiju v repository nebo i presenteru, protože už je to zapouzdřený.
Je to klasická proxy třída. Takové se obzvláště hodí při práci s různými god-objekty. :)
Třída User je přesně to nad čím si lámu hlavu, jestli používat či nikoliv. Dokud jsem nepoužíval ORM tak jsem podobný objekty používal. S tím jak jsem ale začal používat ORM, tak jsem začal používat entity.
Pozor, to není User, ale Users. :) To je ta fasáda.
Samozřejmě by v reálu obsahovala i další metody podle potřeby, já jsem
jen chtěl být ve své ukázce stručný a jít k jádru věci. Já do názvu
„fasádních“ tříd slovo Facade
nedávám, pojmenovávám je
ala Users
, Orders
, Books
atp.
S čím pak pracuješ např. v presenteru/šabloně? S entitou User nebo s objektem User, který jsi uvedl?
(nebo je entita User a objekt User to samé, jen jsi ořezal deklarace properties?)
Jakou zodpovědnost by měla mít třída User? Jen uložení celého pavouka entit souvisejících s uživatelem nebo i jejich čtení a předávání dál?
Přestal jsem používat Nette presentery :–P (bylo by to na delší povídání), ale v té části aplikace, která má na starosti to, k čemu se běžně používají presentery, pracuji s entitami a standardně s těmi „fasádními“ třídami. Je-li aplikace jednoduchá (anebo se mi doménovou logiku podaří elegantně umístit přímo do entit), tu fasádní vrstvu vynechávám a pracuji přímo s konkrétními repositáři. Pokud bych pak potřeboval v takové aplikaci bez fasádní vrstvy vyřešit to, co čem jsi psal, na ten konkrétní úkon bych si nějakou „servisní“ třídu vytvořil. Mé názvosloví (servisní vs. fasádní…) je kompatibilní s tímto článkem.
Ty to takhle používáš nebo jsi jen vytvořil nějaký příklad?
Já se hodně rozhoduji podle konkrétní aplikace. Velmi jednoduché moc neprožívám a pracuji v „presenterech“ přímo s repositáři. Doménovou logiku se vždy snažím mít co nejvíc v entitách (v duchu DDD), model se snažím mít co nejméně „anémický“. To, co se mi ale do entit v takové aplikace nepodaří vměstnat (nebo vhodným způsobem do repositářů), řeším pomocí berliček v podobě servisních tříd.
Je-li aplikace trochu košatější, snažím se zavést plnohodnotnou
fasádní vrstvu, jejíž třídy vytvářejí API modelu. Pak už samozřejmě
v „presenterech“ nepracuji přímo s repositáři, ale s těmi fasádními
třídami. Používám konkrétní repositáře (na rozdíl od například
Kdyby\Doctrine) a query objety (hodně podobně, jako Filip v Kdyby\Doctrine,
jen s tím logickým rozdílem, že mé query objekty se
překládají do sekvence volání nad instancí
Fluent
).
Proxy třídu pro transakce používám.
Editoval Tharos (2. 1. 2014 9:50)
před 6 lety
- Filip111
- Člen | 244
@Tharos:
Teď už mi to konečně dává smysl – vlastně jsem jsem to vyřešil
(ještě před založením tohoto topicu) stejně a předal do fasády,
jednotlivé entity k uložení. Budu se toho držet i nadále.
Díky za odpovědi.
Ještě mě zaujala zmínka o presenterech nepresenterech :). Neměl bys
chuť o tom ještě něco napsat?
Zajímalo by mě, jestli důvodem byla znovupoužitelnost nebo nějaká omezení
presenterů, případně co ti to přineslo za výhody (asi v novém topicu).
Ale nechci tě zbytečně zdržovat.
před 6 lety
- Šaman
- Člen | 2275
Ohledně těch presenterů jsem se chtěl taky zeptat, ale pak jsem to neudělal, protože naprosto klíčové k životaschopnosti LM je vytvořit dokumentaci, což může udělat jen Tharos. Takže se zeptám až potom :)
Lehce offtopic: @Tharosi plánuješ přijít na lednovou posobotu? Vypadá to, že bude zajímavá. A ve volném čase bychom mohli nad pivem probrat pár dotazů na LM.
před 6 lety
- Filip111
- Člen | 244
@Tharos, Šaman:
souhlas, nejdřív dokumentace, zeptáme se potom :)