Tweetjeim

Legújabb hozzászólások

szjani

2014. április 18. 22:07

Köszi! Képeket valóban akartam, de nincs normális eszközöm hozzá, ami igényes kimenetet is produkál. :( - Hexagonal arch...

inf3rno

2014. április 18. 14:23

Jó cikk, nekem teljesen érthető volt, szépek a példák is. Annyi megjegyzésem lenne, hogy egy kép többet mond ezer szónál...

Jordan

2014. április 1. 17:31

Interesting project. I recommend using ORM Designer (www.orm-designer.com)in conjunction with Doctrine. It's a great to...

mindegy

2014. február 4. 14:55

Tetszik! A DDD-ről, CQRS-ről még nem hallottam de ha ezekkel így lehet szoftvert fejleszteni, akkor rossz már nem lehet....

szjani

2013. június 7. 21:38

"Maga az event egy string..." - itt valóban pongyolán fogalmaztam, javítom. A több esemény kezelésére a példa:...

Intelligens eseménykezelés

Manapság minden valamirevaló keretrendszerben elérhető valamilyen eseménykezelő, ami a publish/subscribe mintát követi. Ezekkel az eszközökkel flexibilisebb alkalmazásokat írhatunk, alacsonyabb lesz a "coupling" az osztályain között.

Lehetőségek tárháza...

Egy ilyen eseménykezelőt akartam volna integrálni DDD-s környezetbe. Megnéztem, milyen lehetőségeim vannak és arra jutottam, hogy többé-kevésbé ugyanazt tudja minden framework. De nézzünk megy egy példát a Symfony-ból:

Magát az eventet egy stringgel azonosítjuk, jelen esetben 'foo.action'-nel. Valamint explicit módon megadjuk, hogy az említett azonosítójú eventeket a $listener objektum 'onFooAction' metódusa tudja lekezelni. Ezzel nekem van egy óriási problémám: a lehetőség, hogy elgépelem akár az event, akár a metódus nevét. Ráadásul mi történik egy refaktorálás során? Semmi, észben kell tartani, hogy hohó, ott is át kell ám írni a stringet!

Gondoltam egyet, és megnéztem pár javas event bus megvalósítást, nevesül a Google féle Guava projektet, valamint az Axon frameworkben lévőt. Mindkettőből merítettem ötleteket (előbbiből talán többet) és nekiláttam PHP-ben. Így született a predaddy.

Eventek

Az eventek objektumok. Az event interfészt kell implementálnunk. Technikai és kényelmi okokból ez az ObjectInterface-re épül, amelyből a java-s Object osztályból ismert jónéhány metódus köszön vissza. Ezeket implementálhatjuk mi magunk is, de egyszerűbb az Object osztályból származtatnunk az event osztályunkat. Ha még lustábbak vagyunk, az EventBase-t is felhasználhatjuk, amiben minden szükséges metódus implementálva van. Egy szó mint száz, az eventek szerializálhatók, azonosítóval vannak ellátva, valamint tartalmaznak egy időbélyeget a létrehozásuk idejéről.

EventHandler

Az események kezeléséhez az EventHandler interfészt kell implementálnunk. Az viszont üres! A eseményeket lekezelő metódusainkat úgy nevezzük el, ahogy szeretnénk, nincs megkötés. Ráadásul egy EventHandler osztályban több ilyen metódus is szerepelhet, amelyek eltérő eseményeket is le tudnak kezelni. És hogy mondjuk meg az event busnak, hogy mely eseményre mely metódust hívja? Annotációval. A predaddy event bus a regisztrált event handlerek metódusait scanneli, és értelmezi őket. Nekünk mindössze a @Subscribe annotációval kell ellátnunk a metódusainkat, paraméterül pedig egy typehinttel ellátott eventet várunk. Ezen typehint alapján fogja tudni az event bus, hogy az adott metódus mely eventet tudja feldolgozni.

Event typehint

Az Event osztályainkat tetszőleges módon származtathatjuk egymásból, komplex hierarchiát alakíthatunk ki. A predaddy az elvárt módon kezeli akár az absztrakt, akár az interfész typehintet is. Vagyis ha egy absztrakt osztályból származó összes event típust fel szeretnénk dolgozni egy eseménykezelőben, akkor nem kell minden altípusra létrehozni egy metódust, hanem elég egyet, amiben az event-ök ősosztályát adjuk meg. Tehát ha az "Event" típust adjuk meg egy eseménykezelő metódusnál, akkor az minden eseményre meg fog hívódni.

DeadEvent - ha valamit nem kezeltünk le

Előfordul, hogy szándékosan, vagy valamilyen hiba hatására egy eseményhez nem regisztráltunk egy eseménykezelőt sem. Ebbent az esetben a predaddy becsomagolja azt egy DeadEvent objektumba és elküdi a handlereknek. Érdemes például loggolási célból egy DeadEvent handlert beregisztrálni, így könnyebb a hibakeresés.

Aszinkron és tranzakciófüggő események

PHP-ben sajnos nem lehet többszálú programokat írni, bár egyesek szerint ez talán jól is van így. A lényeg, hogy ily módon nem egyszerű aszinkron végrehajtást elérni. Viszont az mf4php-ben már megoldást adtam az említett problémákra, így készítettem egy Mf4PhpEventBus-t, ami kihasználja az abban lévő lehetőségeket. Ez az implementáció önmagában egy MessageListener is, ami arra a queue-ra iratkoztatja fel magát, amire az eseményeket is küldi. Vagyis a predaddy-s eseményeket mf4php-s Message-be rakja, azt elküldi az mf4php-nek, az visszaküldi a busnak és az továbbitja őket az eseménykezelőknek. Ezzel az mf4php-s kitérővel elérhető az, hogy az üzeneteink akár tranzakciókhoz legyenek kötve, akár aszinkron módon kerüljenek feldolgozásra. Utóbbihoz a beanstalk implementációt ajánlom, ami jelenleg az egyetlen megoldás (szívesen veszek egyéb mf4php implementációkat is).

Mint említettem, az mf4php az eventeket Message-be csomagolja. A konkrét mf4php implementáció támogathat extra funkciókat, mint például késleltetés, priorizálás, időlimitáció. Saját ObjectMessageFactory implementációval beleszólhatunk ebbe a folyamatba és adott event típusonként elkészíthetjük mi magunk is a kívánt Message objektumot.

Aggregate root - DDD

A predaddy-ben lévő event bus nem csak DDD-s projektben használható, de biztosít számunkra néhány plusz osztályt ha mégis DDD-t használunk. Az aggregate root osztályokat származtassuk az AggregateRoot osztályból és statikus módon adjuk át az osztálynak a használni kívánt event bust. A statikus raise() metódussal küldhetünk az osztályon belülről eventeket, melyeket a DomainEvent osztályból kell származtatnunk. Ilyen felhasználás esetén mindenképp javaslom az Mf4PhpEventBus-t valamilyen TransactedMessageDispatcher-rel karöltve, hogy az elsütött eventeket csak és kizárólag sikeres tranzakció után dolgozzuk fel.

Végszó

Meg kell jegyeznem, hogy a predaddy - mivel annotációt használ - erősen támaszkodik a reflectionre. Vagyis valószínűleg lassabb, mint az említett Symfony-s módszer. Bár teljesítmény teszteket nem futtattam, véleményem szerint olyan kicsi lehet ez az overhead, hogy nekem megéri a kényelmesebb, és hibamentesebb megoldás. Remélem nektek is! :)

  • Az e-mail cím nem jelenik meg az oldalon, bizalmasan kezelem.

szjani

2013. június 7. 21:38

"Maga az event egy string..." - itt valóban pongyolán fogalmaztam, javítom. A több esemény kezelésére a példa: https://github.com/szjani/predaddy/blob/master/sample/MultipleEventHandling.php

szjani

2013. június 7. 21:35

A zárójeles részben tévedsz, nézd meg a példákat. Tetszőleges számú eseménykezelő metódust definiálhatsz egy osztályon belül is. Az, hogy az esemény és annak azonosítója külön van választva, eleve nem túl logikus. A konstansok pedig ugyanúgy nem segítenek rajtad refaktorálás esetén, valamint a bennük tárolt stringet ugyanúgy elírhatod :)

Tgr

2013. június 7. 18:51

A Symfony eseménykezelőjében természetesen objektumok az események (minden valamirevaló eseménykezelő komponensben azok, hiszen így tudsz csak adatokat küldeni a handlereknek). Az esemény *típusa*, amit a feliratkozásnál meg kell adni, nem az, de hát erre találták ki a konstansokat. (Az annotációval való feliratkozás viszont kétségkívül elegáns megoldás, bár jelen formájában egy eseménykezelő osztállyal csak egyetlen eseményre tudsz feliratkozni, ami nem túl kényelmes.)

Az adatbázis kezeléshez Doctrine-t használok Az oldal Zend Framework-re épül