Closed ghost closed 9 years ago
Dokumentáció egyelőre az van, ami githubon is elérhető, tehát a főoldali README, valamint a belőle eléréhető 3 további.
Az issue trackeres példa egyben ES-t is használ. A repository megkapja az aggregatehez tartozó, perzisztált eventeket, és rekonstruálja azokból az aggregatet.
Immediate consistency 2 phase commit nélkül nem oldható meg 2 eltérő DB esetén, viszont az issue trackeres példa valóban több rejtett hibát tartalmaz. Sokkal biztosabb megoldás az, ha a write storage és az event storage ugyanaz (ES-nél ez ugye adott), és itt egy tranzakcióban kerülnek lementésre az eventek és az aggregatek. Ezután pedig egy job elkezdheti feldolgozni a még feldolgozatlan eventeket. Ezzel a módszerrel biztosítható, hogy az eventek sorrendje mind mentésnél, mind feldolgozásnál megőrzi az eredeti sorrendet és a read storage is konzisztens marad. Részletesebben az Event Storage as a Queue résznél.
Commandok feldolgozásával kapcsolatban tudsz visszajelezni, természetesen eventual consistency esetén csak a command oldai feldolgozás eredményéről tudsz nyilatkozni. A command handler (meg általában bármelyik message handler) adhat vissza értéket, illetve dobhat kivételt, amiket a hívó kód meg tud kapni. Ez MessageCallback-kel érheted el.
Még annyit, hogy az issue trackeres alkalmazás tényleg csak egy alap koncepciót mutat be, valamint predaddy 2-re épít. Bár még nem releaseltem a 3.0-át, javaslom annak a használatát.
"Dokumentáció egyelőre az van, ami githubon is elérhető, tehát a főoldali README, valamint a belőle eléréhető 3 további." Ohh most látom tényleg. Én csak átfutottam felette a "documentation" szót keresve...
"Az issue trackeres példa egyben ES-t is használ. A repository megkapja az aggregatehez tartozó, perzisztált eventeket, és rekonstruálja azokból az aggregatet." - Ja láttam, hogy snapshot-ol, amiről nagyjából tudtam, hogy valami ilyesmit jelent.
"Sokkal biztosabb megoldás az, ha a write storage és az event storage ugyanaz (ES-nél ez ugye adott), és itt egy tranzakcióban kerülnek lementésre az eventek és az aggregatek." - A write storage-be mi kerül ilyenkor? Olyasmik, amik validáció szempontjából fontosak, pl email cím, ha unique kell, hogy legyen? Mi van, ha elhasal a read storage, hogyan biztosítod, hogy a megfelelő event-től kezdje újra a job a read storage módosítását, vagy ne akadjon el az egész rendszer amiatt, mert egy event feldolgozása közben valami bug jelentkezett? Elméletileg ezt is egy bus-on kéne megoldani valahogy, jól sejtem?
"A command handler (meg általában bármelyik message handler) adhat vissza értéket, illetve dobhat kivételt, amiket a hívó kód meg tud kapni. Ez MessageCallback-kel érheted el." - Ahm, szóval akkor nem feltétlen kell külön bus rá. Ez jó.
"Még annyit, hogy az issue trackeres alkalmazás tényleg csak egy alap koncepciót mutat be, valamint predaddy 2-re épít. Bár még nem releaseltem a 3.0-át, javaslom annak a használatát." - Persze, láttam, hogy teljesen átrendezted a könyvtár struktúrát, rendezettebb lett az egész... Gondolom majd összeszórod ezt az issue tracker-t arra is, ha release-elted...
Olvasgatok dokumentációt, aztán visszajelzek jövő héten valamikor. Van egy elméletem, amit szeretnék egy proof of concept alkalmazással igazolni. REST és CQRS hatékony összekötéséről van szó.
Ha nem használsz ES-t, akkor a write storage-ba természetesen bekerül minden aggregate, ES esetén viszont csak az eventek. A read storage szinkronizáció során validációs hiba nem igazán adódhat, mivel validált adatokkal kell felülírni a régieket. A background job a legrégebbi, még fel nem dolgozott eventet olvassa ki és dolgozza fel, ez biztosítja mind a sorrendiséget, mind az esetleges hibás feldolgozások újraindítását (ami mondjuk hálózati hiba miatt adódott). Ha valamilyen szoftver bug miatt kiderül, hogy inkonzisztens a read storage, "csak" újra kell pörgetni az összes mentett eventet. Uniqueness validációról (ami csak ES esetén merül fel, mint probléma) Greg Young véleménye.
Nem teljesen tiszta, hogy ES, vagy ES nélkül akarsz CQRS-t használni.
Mindenképp ES-el, mert nem szeretném már az elején megkötni, hogy milyen read storage-t használok. Ami talán a kommunikációs zavart okozza, hogy
Na ehhez a másodikhoz jobban illik inkább egy event alapú kommunikáció mondjuk websocket-el. Bár REST-el is meg lehet oldani pollinggal a válasz elkérését. Engem inkább az első részletei érdekelnének, tehát hogy a read cache-t állandóan szinkronban tartom a write cache-el distributed transaction-öket használva (ha külön adatbázisban vannak), vagy valahogy kezelve a read storage esetleges kiesését a rendszerből. Egyelőre nem áll össze a pontos kép, hogy mi zajlik a háttérben, és hogyan lehet a hálózati hibákat kezelni. Mondjuk distributed transaction-el egyáltalán nincs tapasztalatom, csak tudom, hogy azt kell használni, ha több helyre akarok egyszerre írni, és szeretném biztosítani a konzisztenciát.
Azzal tisztában vagyok, hogy uniqueness-nél nagyobb rendszereknél eventual consistency-t használnak, és read storage-ből olvassák ki a validációs adatokat, majd esetleg ha mégis becsúszik egy hiba nagy ritkán, akkor kompenzálnak, vagy egyáltalán nem foglalkoznak a kérdéssel, mert kicsi az esélye. Pl egy email címes regisztrálásnál kompenzálhatnak úgy, hogy elküldik levélben, hogy bocs, de már regisztráltál, ha elfelejtetted a jelszavad, akkor ide kattintva tudsz resetelni. Vagy esetleg egyáltalán nem küldenek levelet, mert valószínűleg duplán kattintottak a regisztrációs űrlap küldésekor, etc... Viszont egy immediate consistency-s rendszerben elméletileg nem szükséges kompenzálni, mert a concurrency kezelés tranzakcióval van megoldva. Van esetleg más előnye is a kompenzálásnak, pl ha nem ACID a tranzakció amiben a mentés és read frissítés egyszerre megy el, vagy nem tudok összeszórni egy 2 phase commit-ot külön adatbázisokra, akkor érdemesebb kompenzálást használni immediate consistency-nél is? Bár akkor már nem beszélünk immediate consistency-ről, mert egymás után fut le a két storage frissítése, csak nem külön process hívja a job-ot, hanem ugyanaz. mint ami a write storage-t is írja.
Szerintem nagyon előre szaladtunk, jobb lenne talán először megismerkednem a rendszereddel, meg végigolvasni a linkeket, amiket küldtél... Lehet, hogy az a baj, hogy túlzottan ragaszkodom az immediate consistency-hez, passz... Nehezen tudom elképzelni, hogyha rámegyek mondjuk egy új item hozzáadására, akkor ne jelenjen meg azonnal, hogy hozzá van adva, és tudjak további dolgokat beállítani rajta, amik a létrehozáshoz esetleg nem is kellettek...
Amikor commandot küldesz (POST, PUT, DELETE), akkor az valami írási műveletet indukál. Mikor a requestre válaszolni kell, még nem biztos, hogy szinkronban van a query oldal. De miért fontos ez? Ha hiba történét, arról tudsz értesítést küldeni, 200 OK-ot tudsz küldeni, ha pedig egy új aggregate jött létre, az ID-t is vissza tudod küldeni. Egyikhez sem kell read storage. De te REST-ben sokkal inkább otthon vagy, úgyhogy lehet, hogy valami felett elsiklottam. Viszont ha az a gond, hogy a fenti request után a kliens egyből húzná le az adatot és még nem elérhető... hát ez van, ez az eventually consistency :)
Ha két külön DB-be írsz és nem használsz 2PC-ot vagy 3PC-ot, akkor az nem immediate consistency, még ha a két írás egymás után történik is. Közben egy másik request csúnyán bele tud szólni a dolgokba (race conditions). Mivel eventet nem törlünk (az event store egy audit log is egyben, már csak ezért sem), ezért meg nem történté nem tudod tenni azt, amit a write storage-ban lementettél. Úgyhogy a kérdésedre válaszolva, szerintem igen.
Viszont ha az a gond, hogy a fenti request után a kliens egyből húzná le az adatot és még nem elérhető... hát ez van, ez az eventually consistency :)
Persze, erről van szó. De van megkerülő megoldás, visszaküldesz egy 202-accepted header-t, amivel jelzed, hogy elfogadtad a kérést, de még fel kell dolgozni. Az id-t már tudod, úgyhogy tudsz csatolni mellé egy linket, amit pollolhat a kliens egészen addig, amíg nem jön válasz...
Ha két külön DB-be írsz és nem használsz 2PC-ot vagy 3PC-ot, akkor az nem immediate consistency, még ha a két írás egymás után történik is.
Ja, én is erre gondoltam. Akkor egyszerűbb kompenzálni, vagy mérlegelni, hogy mi a következménye ilyen eseményeknek, és megéri e erre bármilyen kódot írni. A multi phase commit-ba nem ástam bele nagyon magam, de az jött le, hogy nem is nagyon van rá lehetőség, ha nem direkt úgy tervezik az adatbázist, hogy támogassa. Lehet, hogy átsiklottam valami felett...
Olvasgatok dokumentációt, aztán visszajelzek jövő héten valamikor. Van egy elméletem, amit szeretnék egy proof of concept alkalmazással igazolni. REST és CQRS hatékony összekötéséről van szó.
Úgy néz ki, hogy a dolog nem működőképes. Arról lett volna szó, hogy az ubiquitous language (értsd domain model) és a REST application specific language-e között automatikusan generáltatok egy ontology alignment-et. Ez körülbelül annyit jelentett volna a gyakorlatban, hogy egy gombnyomással (vagy minimális munkával) lett volna REST API a domain model-hez. Unfortunately reality is not so forgiving (mai is tanultam egy új kifejezést). Azért nem működik a dolog, mert a DDD alapvetően objektum orientált, a REST viszont inkább procedurális, szóval csak CRUD-nál lenne lehetséges egy az egyben összekötni a REST resource-okat az entity-kkel, ami meg ugyebár anaemic domain model-t eredményezne. Ezt részleteiben leírom itt is: http://stackoverflow.com/questions/26049934/is-it-possible-to-do-ddd-and-rest-interface-and-language-mapping de megkértem valakit, hogy nézze át, hátha hülyeséget beszélek (kicsi rá az esély).
Beleásom magam a DDD részébe is a dolognak, aztán valószínűleg a későbbiekben írok egy kiegészítést a predaddy-hez, amivel lehet REST API-kat is csinálni, de bonyolult a dolog...
Emellett agyalok még a kliens oldali DDD megvalósításán is, de ahhoz előbb végeznem kellene a messaging-es js projektemmel, ami megoldaná az aszinkron kód olvashatóságával kapcsolatos problémákat. Ha esetleg van kedved js-ben kódolni egy kicsit, akkor beszállhatsz. :-) Egyébként a kódolás a legkisebb része a dolognak sajnos. Nagyon nehéz elsőre átlátni, hogy egy általános célú projektnél milyen funkciókra van szükség, és mire nincs, illetve, hogy milyen felülettel lenne érdemes megvalósítani. Na legalábbis nekem... Most valószínűleg nulláról újraírom az egészet, mert DDD-be egy kicsit beletanulva illetve egy proof of concept példa alapján már úgy látom, hogy teljesen mást kellene, mint ami a megvalósítás terén az alap elgondolás volt... Az elmélete viszonylag egyszerű: mivel js-ben nincsenek aszinkron nyelvi elemek (egyedül a yield, ami haszontalan), ezért szinkron js kóddal megközelítve sosem lehet olvasható aszinkron kódot írni. Mindig ott lesz a callback hell, bármit csinálsz. Az egyedüli lehetőség a probléma megoldására, ha ugyanúgy oo modellezzük, mint bármi mást. Ezt többféleképp lehet, az egyik, hogy minden egyes nyelvi elemet, mint pl if-else, ciklusok, stb... modellezünk aszinkron formában is, és kód írása helyett ezeket az elemeket példányosítjuk, és építünk belőlük egy baromi nagy objektum gráfot. Ezt dataflow based programming-nek hívják, és van is ilyen projekt, noflo a neve. Szerintem baromi nagy overengineering mindenhol ezt a megoldást használni, helyette érdemesebb csak néhány egymással kompatibilis felülettel rendelkező aszinkron komponenst (pl view-ok, model-ek, history plugin, REST kliens, websocket kliens, stb...) leírni, és azokat összekötni. Így ezek egymás között tudnak aszinkron módon üzeneteket küldeni a feldolgozás meg ugyanúgy mehet, mint egy szinkron js kódban. A legjobb példa ilyen megoldásra a unix stream-ek. Másolásnál mondjuk ahelyett, hogy csinálsz egy read stream-et, aztán annak a callback-jében egy write stream-et, és így tovább, már az elején létrehozod mindkét stream-et, aztán csak közéjük csapsz egy pipe-ot és kész. Nyilván ami létrejen az szintén egy irányított objektum gráf. Na ilyet szeretnék a domain model és az aszinkron külsőségek (portok: dom, push state, ajax, websockets stb...) vagy akár több domain model közé. Na sok lesz a rizsa, jobb, ha megyek aludni...
Please english
On Friday, October 10, 2014, Jánszky László Lajos notifications@github.com wrote:
Olvasgatok dokumentációt, aztán visszajelzek jövő héten valamikor. Van egy elméletem, amit szeretnék egy proof of concept alkalmazással igazolni. REST és CQRS hatékony összekötéséről van szó.
Úgy néz ki, hogy a dolog nem működőképes. Arról lett volna szó, hogy az ubiquitous language (értsd domain model) és a REST application specific language-e között automatikusan generáltatok egy ontology alignment-et. Ez körülbelül annyit jelentett volna a gyakorlatban, hogy egy gombnyomással (vagy minimális munkával) lett volna REST API a domain model-hez. Unfortunately reality is not so forgiving (mai is tanultam egy új kifejezést). Azért nem működik a dolog, mert a DDD alapvetően objektum orientált, a REST viszont inkább procedurális, szóval csak CRUD-nál lenne lehetséges egy az egyben összekötni a REST resource-okat az entity-kkel, ami meg ugyebár anaemic domain model-t eredményezne. Ezt részleteiben leírom itt is: http://stackoverflow.com/questions/26049934/is-it-possible-to-do-ddd-and-rest-interface-and-language-mapping de megkértem valakit, hogy nézze át, hátha hülyeséget beszélek (kicsi rá az esély).
Beleásom magam a DDD részébe is a dolognak, aztán valószínűleg a későbbiekben írok egy kiegészítést a predaddy-hez, amivel lehet REST API-kat is csinálni, de bonyolult a dolog...
Emellett agyalok még a kliens oldali DDD megvalósításán is, de ahhoz előbb végeznem kellene a messaging-es js projektemmel, ami megoldaná az aszinkron kód olvashatóságával kapcsolatos problémákat. Ha esetleg van kedved js-ben kódolni egy kicsit, akkor beszállhatsz. :-) Egyébként a kódolás a legkisebb része a dolognak sajnos. Nagyon nehéz elsőre átlátni, hogy egy általános célú projektnél milyen funkciókra van szükség, és mire nincs, illetve, hogy milyen felülettel lenne érdemes megvalósítani. Na legalábbis nekem... Most valószínűleg nulláról újraírom az egészet, mert DDD-be egy kicsit beletanulva illetve egy proof of concept példa alapján már úgy látom, hogy teljesen mást kellene, mint ami a megvalósítás terén az alap elgondolás volt... Az elmélete viszonylag egyszerű: mivel js-ben nincsenek aszinkron nyelvi elemek (egyedül a yield, ami haszontalan), ezért szinkron js kóddal megközelítve sosem lehet olvasható aszinkron kódot írni. Mindig ott lesz a callback hell, bármit csinálsz. Az egyedüli lehetőség a probléma megoldására, ha ugyanúgy oo modellezzük, mint bármi mást. Ezt többféleképp lehet, az egyik, hogy minden egyes nyelvi elemet, mint pl if-else, ciklusok, stb... modellezünk aszinkron formában is, és kód írása helyett ezeket az elemeket példányosítjuk, és építünk belőlük egy baromi nagy objektum gráfot. Ezt dataflow based programming-nek hívják, és van is ilyen projekt, noflo a neve. Szerintem baromi nagy overengineering mindenhol ezt a megoldást használni, helyette érdemesebb csak néhány egymással kompatibilis felülettel rendelkező aszinkron komponenst (pl view-ok, model-ek, history plugin, REST kliens, websocket kliens, stb...) leírni, és azokat összekötni. Így ezek egymás között tudnak aszinkron módon üzeneteket küldeni a feldolgozás meg ugyanúgy mehet, mint egy szinkron js kódban. A legjobb példa ilyen megoldásra a unix stream-ek. Másolásnál mondjuk ahelyett, hogy csinálsz egy read stream-et, aztán annak a callback-jében egy write stream-et, és így tovább, már az elején létrehozod mindkét stream-et, aztán csak közéjük csapsz egy pipe-ot és kész. Nyilván ami létrejen az szintén egy irányított objektum gráf. Na ilyet szeretnék a domain model és az aszinkron külsőségek (portok: dom, push state, ajax, websockets stb...) vagy akár több domain model közé. Na sok lesz a rizsa, jobb, ha megyek aludni...
— Reply to this email directly or view it on GitHub https://github.com/szjani/predaddy/issues/2#issuecomment-58735758.
@cordoval These are just basic questions about DDD, CQRS and other topics. So they are not closely related to predaddy... If you are really interested I can give you a short translation.
would be nice thanks
@cordoval As you wish! :-)
POST /transactions {account1, account2, ...}
with account1.sendMoney(account2, ...)
in your domain model. So you don't necessary have an entity for the "transaction" resource. It is relatively easy to write such examples. I intend to write a REST module to predaddy which will help to generate Hydra REST APIs using annotations of domain objects, services, etc..., but it won't be an easy task... (next year probably)thanks @inf3rno
FYI: Although the concept is that domain events are dispatched to handlers just after the transaction has committed, this behavior can be overridden not just with different configuration but with NonBlockable
events. These events are dispatched immediately, within the same transaction.
Since commands can be sent in domain event handlers, predaddy now supports nested transactions which means it won't start a new transaction if there is an open one.
These two new things provide you the chance to use loops within one transaction (command -> command handler -> aggregate change -> domain event -> domain event handler -> command -> ...).
We were talking about the predaddy-issuetracker project. I have just fixed the 3.0 branch, now it works with the latest predaddy.
Van fórum és dokumentáció predaddy-hez? Nem akarom telefirkálni az issue tracker-t, mert általában mindenki elküld a ..., ha kérdést írok ide a keretrendszerükről.
Érdekelne, hogy hogyan lehet megoldani több database-nél az immediate consistency-t? Nekem csak 2 phase commit, és hasonló komplikált dolgok jutottak eszembe, viszont jóval tisztábbá tenné a képet, ha külön adatbázis(ok)ba menne a query cache és máshova az event sequence. A PHP esetében sajnos nincs eléggé megtámogatva a daemon-ok írása, hogy lehetne eventual consistency-vel trükközni.
Néztem az issue tracker példádat.
Nem igazán látszik benne, de jól fogom, hogy a repository amikor elkéred tőle a már létező issue-t, akkor benyeli a domain event-et, ami a példányosítással jár?
Nekem még hiányzott belőle egy visszacsatolás hibákkal, illetve a sikerességgel kapcsolatban. Komolyabb alkalmazásoknál, mondjuk REST-nél azért illik visszajelezni, hogy mi történt a kéréssel, legalább annyit, hogy befogadta a rendszer. Gondolom ez is megoldható, csak nem akartál annyira részletekbe menni.
Egyébként tetszik a koncepció. Én azt hittem ennél sokkal komplikáltabb egy CQRS implementálása. Biztos, hogy nem így álltam volna neki...