ugrás a tartalomhoz

Boundaries

MadBence · 2014. Már. 26. (Sze), 11.57
Gary Bernhardt előadása a helyes komponensszervezésről
 
1

Ez komoly, hogy nem lehet

inf3rno · 2014. Már. 26. (Sze), 14.22
Ez komoly, hogy nem lehet online megnézni?
2

Végignéztem, érdekes. Nem

inf3rno · 2014. Már. 26. (Sze), 15.01
Végignéztem, érdekes. Nem értem... :D

Most jelenleg én is sűrűn használok value objecteket rétegek, modulok közötti adatátadásra, de hogy erre a koncepcióra tervezzek egy rendszert, azt valahogy nem tudom elképzelni. Valszeg én értettem félre valamit...

Erősen kétlem, hogy ki lehetne váltani az integrációs tesztelést bármivel, legalábbis a logika azt mondja, hogy ahol unit test-et írsz objektumra, ott az objektumok kölcsönhatását is vizsgálni kell, arra meg integrációs tesztek kellenek...
3

A srác feltalálta a DDD-t,

szjanihu · 2014. Már. 27. (Cs), 01.01
A srác feltalálta a DDD-t, hexagonal architecturet, pub/sub-ot, fluent interfacet, meg még ki tudja hány jól ismert dolgot.

Nem írt tesztet a "shell"-hez, mondván abban nincs logika. Pont az ilyenekhez kell írni integrációs tesztet. Például hiába megy a unit teszt, ha a perzisztálás nem működik, mert mondjuk elrontottam valamit a configban (mondjuk rosszul mappelek egy fieldet; vagy nem unique, ami alapján egyedi találatot keresek, stb.).

Logikát tartalmazó objektumokat passzolgatni layerek között viszont kétségkívül egyedi megoldás. Rossz is.
4

Jólvan, legalább valaki érti,

inf3rno · 2014. Már. 27. (Cs), 01.11
Jólvan, legalább valaki érti, hogy miről van szó. :D
Tudnál szájbarágósabb linkeket ajánlani a fentebbi témákról? A hexagonal architecture-t nagyjából vágom, a többinek viszont még utána kellene olvasnom.
5

A pub/sub a publish/subscribe

szjanihu · 2014. Már. 27. (Cs), 01.29
A pub/sub a publish/subscribe rövidítése. Események dobása elkapása egy objektumon keresztül (bus). A legtöbb PHP-s frameworkben már implementálva, az én annotációs megoldásomat azt hiszem talán épp te küldted be itt a blogmarkokba.

A fluent interfaces, amikor adatmódosító, tehát jó esetben void típusú metódusban visszaadod az aktuális objektumot (return $this), így egymás után lehet hívni ezeket a metódusokat: $user->setName($name)->setEmail($email)->...

A DDD már "kicsit" bonyolultabb. Teljesen ellentétes a még mindig divatos "pakoljunk minden logikát stateless servicekbe" felfogással annak ellenére, hogy több, mint 10 éves dologról van szó. Helyette a domain modelbe rakja mindezt, illetve amit csak lehet és egy mindentől független központi magot (domain réteg) képez, ami könnyen tesztelhető. Ez reprezentálja, illetve hordozza mindazt az információt, ami az üzleti folyamatokban szerepet játszik. Ha ehhez még hozzávesszük a CQRS-t, akkor már ott leszünk, mint amit az előadásban előnyként hallhattunk, csak épp domain modellt nem küldözgetjük szanaszét minden rétegbe (ha jól értettem, ő ezt tette - a rubyhoz nem értek). Egy prezentáció tőlem a témában.
6

Köszi. Nem tudtam, hogy

inf3rno · 2014. Már. 27. (Cs), 01.44
Köszi.

Nem tudtam, hogy pub/sub-nak rövidítik meg hogy DDD-nek hívják ezt a típusú tervezést... A fluent interface-el kapcsolatban fenntartásaim vannak, túl sokat olvastam a law of demeter-ről. :d

Tudnál többet mondani arról, hogy miért nem jó ötlet szétküldözgetni a domain model-t, illetve hogyan kell megvalósítani egy ilyen elvekre épülő rendszert? Olvasgattam a clean architecture-el meg a hexagonal architecture-el kapcsolatban elég sokat, az elveket nagyjából értem, de nem igazán jön át, hogy a gyakorlatban mindez hogyan néz ki, illetve a példák nyelveit sem feltétlen értem. Én a kapcsos zárójel világában nőttem fel, és falra mászok attól, hogy az eltérő behúzásoknak különösebb jelentése van :D Úgy látszik nekem is vannak korlátaim...

Jelenleg csináltam egy olyan rendszert, aminél modulokra osztottam a kódot. Minden modulnak van egy DI containere, ami a többi modulból importál be objektumokat vagy a többi modulnak exportál objektumokat. Ezek további DI containereket használhatnak, amiknek a termékeit már csak modulon belül használom. Csináltam egy külön névteret a modulok között utazgató objektumok interface-einek megadására, illetve a value objecteknek, amikbe csak adat ellenőrzést tettem. A modulok között az import és export ezek alapján az interface-ek alapján történik, tehát nincs szoros csatolás a modulok objektumai között, csak a modulok objektumai és ezek között az interface-ek között. Jelenleg itt tartok, ránézésre egyszerű példán működik a dolog, de még nem bonyolítottam a kódot. Szerinted ez jó koncepció, vagy alapvetően hibás? Hmm talán jobb lenne kitennem github-ra egy kódot, és nyitni neki egy topicot a vélemény kérése témában.
8

A law of demeter teljesen jó,

szjanihu · 2014. Már. 27. (Cs), 09.15
A law of demeter teljesen jó, óvakodni kell a hosszú method chaintől. Viszont fluent interface esetén ha mindig az aktuális objektumot adod vissza, akkor nem lesz törékenyebb a kódod, mintha minden metódushívás előtt leírnád a változó nevét. Builder patternnél nagy divat használni, nem szokott probléma lenni belőle.

Az architekturális felépítésről írtam egy viszonylag hosszabb bejegyzést, githubon van hozzá egy nagyon leegyszerűsített példa is.

A modulok közötti szeparáció - amiről írsz - alapvetően jónak tűnik. A value objectnek DDD-ben meg van a maga jelentése és szintén a domain rétegbe tartozik. Adattovábbításra DTO-t szokás használni (Data Transfer Object), amiben nincs semmi ellenőrzés, és jó, ha immutable.
11

A law of demeter teljesen jó,

inf3rno · 2014. Már. 27. (Cs), 11.27
A law of demeter teljesen jó, óvakodni kell a hosszú method chaintől. Viszont fluent interface esetén ha mindig az aktuális objektumot adod vissza, akkor nem lesz törékenyebb a kódod, mintha minden metódushívás előtt leírnád a változó nevét. Builder patternnél nagy divat használni, nem szokott probléma lenni belőle.


Ja így már világosabb... A law of demeter inkább adatrejtésre irányul, ahelyett, hogy az objektum által tárolt adatokat adnánk vissza, bele kell vinni az adott funkciót az objektum viselkedésébe.

A modulok közötti szeparáció - amiről írsz - alapvetően jónak tűnik. A value objectnek DDD-ben meg van a maga jelentése és szintén a domain rétegbe tartozik. Adattovábbításra DTO-t szokás használni (Data Transfer Object), amiben nincs semmi ellenőrzés, és jó, ha immutable.

A későbbiekben majd felrakok egy példakódot, aztán kérek még külön véleményt a kódról. Van egy olyan kérdésem még, hogy vajon jó e az, ha az adatbázis és a business logic között ugyanaz a típusú DTO megy, mint a business logic és a reprezentáció között? Sok helyen nem érzem szükségét, hogy a business logic-ból belenyúljak bármibe is, és egyszerűen csak továbbítom a lekérés eredményét a reprezentációnak. Valahogy így:

namespace module\businessLogic;

use contract\IArticleInteractor;
use contract\IArticleRepository;
use contract\ContentListDto;

class ArticleInteractor implements IArticleInteractor
{
    /** @var IArticleRepository */
    protected $articleRepository;

    public function __construct(IArticleRepository $articleRepository)
    {
        $this->articleRepository = $articleRepository;
    }

    /** @return ContentListDto */
    public function listContent()
    {
        return $this->articleRepository->selectAllContent();
    }
}
Ez persze idővel fejlődni fog külüönböző szűrőkkel, vagy sorrend beállítással, de nem tudom, hogy ebből mi menjen a business logic-ba, és mi a data access-be. Egyelőre még az elnevezéseket is túl rugalmasan használom, pl a repository pattern nem hiszem, hogy ilyen lenne, és az interactor sem biztos, hogy ezt kellene, hogy csinálja, a contract-et is lehet, hogy más értelemben szokták használni, kicsit még káosz az egész...

Köszi a linkeket, el fogom olvasni őket.
13

Kérdések

Hidvégi Gábor · 2014. Már. 27. (Cs), 12.20
Ha egy programot – amit a fenti kódrészletben alkalmazott elvek szerint írsz meg – átadsz másnak (mert mondjuk lebetegszel, de közben tovább kell vinni), akkor az megy dokumentáció nélkül? Pont te írtad tegnap:
Az ügyfelet szerintem inkább az érdekli, hogy ha lerakod a szoftver elé, akkor tudja használni, és nem az, hogy milyen szuperül működnek a tesztek, vagy hogy teljesen bug mentes a szoftver... Én néha egy kicsit erőltetettnek érzem ezt a tökéletes kódra törekvést.
Nem lesz így túlságosan elbonyolítva a kód? Nem lesznek túlságosan elbonyolítva a tesztek? Mekkora projektnél van szükség ilyen szintű absztrakcióra?
14

Nem lesz így túlságosan

inf3rno · 2014. Már. 27. (Cs), 13.18
Nem lesz így túlságosan elbonyolítva a kód? Nem lesznek túlságosan elbonyolítva a tesztek? Mekkora projektnél van szükség ilyen szintű absztrakcióra?


Bármekkoránál, ha úgy látod, hogy később fejlesztések lesznek, hozzá kell nyúlni, stb... Egy ilyen rendszerben bármikor lecserélheted az összes modulodat, ha tartod a határfelület struktúráját, ami közöttük van. Pl bármikor hozzáadhatok egy másik storage modult, ami fájlrendszerbe, adatbázisba, memory-ba ment, esetleg cookie-ba ment, stb... Vagy bármikor hozzáadhatok egy új delivery method-ot, ami hagyományos webapplication-t csinál az alkalmazás egy részéből, vagy rest apit, vagy soap webservice-t, stb...

Az integrációs tesztek egyszerűbbek lesznek attól, hogy a modulok lazán csatoltak egymáshoz. Ha csak egy modul típust akarsz tesztelni, akkor az összes többi modultól való függőségét kimockolhatod, és tesztelheted egymagában. Pl ha storage modult tesztelsz, akkor kimockolhatod a business logic-ot és a delivery method-ot is. Nem kell foglalkoznod velük... Plusz előny, hogy ha egyszer megírod a storage modulodhoz a teszteket, akkor a későbbiekben ha mondjuk adatbázist válasz, vagy úgy döntesz, hogy fájlrendszerbe mentesz, akkor ugyanazok a tesztek jók lesznek, nem kell egyetlen új tesztet sem írnod. Ha adatbázis migrációt akarsz csinálni, akkor azt szintén megteheted két eltérő adatbázishoz tartozó storage modul példányosításával, és felhasználhatod a két modul felületét a migrációs modulod megírásához. Ami szintén jó, hogy a migrációs modulod független a storage modulod implementációjától, tehát amikor fejleszted, akkor pl csinálhatsz két memory storage modult, ami ugye baromi gyors, és megírhatod a tesztjeidet azokat használva. Ezek a tesztek ugyanúgy érvényesek lesznek az adatbázis migrációs változatra is, mert csak a storage modul felületét fogják használni... Szóval sok előnye lehet az ilyen jellegű tervezésnek... A hátrányai ennek a fajta tervezésnek egyébként ugyanazok, mint az objektum orientált megközelítésnek vagy a polimorfizmusnak: könnyű új típust hozzáadni, de nehéz új metódust hozzáadni, vagy meglévő metódusokat módosítani. Ha a modulok közötti hátérréteghez hozzá kell nyúlnod, akkor minden egyes modult, ami attól függ, muszáj lesz megváltoztatnod, és minden egyes tesztbe bele kell írnod, amire a módosítás hatással lesz. Itt annyiban lehet könnyíteni csak a helyzeten, hogy a már nem használt modulokra megszünteted a supportot... De pl egy MemoryStorage modult mindig érdemes szerintem tartani, ha gyors integrációs teszteket akarsz a tesztek futásáig történő perzisztenciával...

Ha egy programot – amit a fenti kódrészletben alkalmazott elvek szerint írsz meg – átadsz másnak (mert mondjuk lebetegszel, de közben tovább kell vinni), akkor az megy dokumentáció nélkül?


Egyelőre csak kísérletezek ezzel a megoldással, de szerintem dokumentációs szempontból is vannak előnyei. Pl itt a modulok határrétege nálam a contract névtérben van. Ha dokumentációt akarok írni, akkor először leírom a contract névtér interface-eit, DTO-it. Ha azt valaki elolvassa, akkor nagyjából fogalma lesz arról, hogy mit csinál a rendszer. Utána le lehet írni, hogy melyik modul mit implementál a contract-ből, és hogyan. Pl jelen esetben az IArticleRepository-nál leírom, hogy cikkeket tárol, és hogy a selectAllContent (elég hülye neve lett) metódusával az összes letárolt cikket vissza kell adnia ContentListDTO formában. A ContentListDTO-nál meg ugye ott van, hogy az gyakorlatilag egy gyűjtemény a ContentDTO-kból, a ContentDTO-nál meg ott vannak az egyes property-k, azoknál egyesével le lehet írni, hogy milyen tulajdonságai vannak 1-1 cikknek, és így tovább...


Ez a válasz mondjuk az én hitvilágomat tükrözi. A valóság általában sokkal bonyolultabb szokott lenni, mint amire számítok, úgyhogy még gyakorolnom kell, próbálgatni, hogy mire képes... :-)
15

A presentation réteg buta DTO

szjanihu · 2014. Már. 27. (Cs), 13.52
A presentation réteg buta DTO objektumokat kapjon. Úgy látom ez az interactor olvasásra van és teljesíti ezt a feltételt. A IArticleInteractor lényegében a read API része. DDD kontextusban: a repositoryk viszont domain objecteket adnak vissza, nem DTO-kat. Ezekben a domain objectekben van a business logic. Én erősen javaslom a CQRS-t, tehát válaszd szét az író/olvasó folyamatot: előbbinél van domain entitykkel, VOkkal, repositorykkal, stb., utóbbinál csak egyszerű finderek és DTO-k vannak. Erről részletesen írtam a fentebb linkelt írásomban.
16

Ahm szóval az

inf3rno · 2014. Már. 27. (Cs), 15.43
Ahm szóval az ArticleInteractor implementációnak az lenne a feladata, hogy ezekbe a domain objectekbe, mondjuk jelen esetben valami ArticleListEntity-be bekérje az adatokat, aztán konverálja azokat ArticleListDTO-ra? Mitől jobb ez a fajta megközelítés, mint eleve csak az adatot visszaadni a tárolóból?

Lehet, hogy tényleg el kéne olvasnom ezeket a cikkeket, mielőtt kérdezek. Jelenleg megint annyi cikk van nyitva a témában, hogy bele fog telni pár napba, majd jelentkezem.
7

Nem neztem meg a videot de ez

Greg · 2014. Már. 27. (Cs), 07.16
Nem neztem meg a videot de ez alapjan:
This talk is about using simple values (as opposed to complex objects) not just for holding data, but also as the boundaries between components and subsystems.

Mintha nem arrol szolna mint amit te irsz :). Pont hogy nem az objectumokat hanem csak ertekeket passzol a retegek kozott, legalabbis itt az van irva.
Kesobb megnezem majd a videot.
9

A Ruby kódot még olvasni se

szjanihu · 2014. Már. 27. (Cs), 09.19
A Ruby kódot még olvasni se tudom jól, ezért könnyen lehet, hogy tévedtem, nekem viszont úgy tűnt, hogy objektumokat küldözget, ráadásul azokban van alapvetően a business logic is. Úgy vettem ki, hogy ez attól nem "complex", hogy nem foglalkozik az adat beolvasással, kiírással, email küldéssel, stb.

De lehet nagyon mellélőttem. Ezesetben mea culpa.
10

A videóban említi, hogy ezek

MadBence · 2014. Már. 27. (Cs), 11.21
A videóban említi, hogy ezek kb annyit tudnak, mint egy sima hash.
12

De lehet nagyon mellélőttem.

inf3rno · 2014. Már. 27. (Cs), 11.30
De lehet nagyon mellélőttem. Ezesetben mea culpa.


Nem biztos az, 2012 óta azért eltelt 2 év, és szinte sehol nem írnak erről a "forradalmi áttörésről". Szóval vagy senki nem érti, hogy miről van szó, vagy mégsem akkora áttörés...