-
-
Save Isinlor/3a9134c41b59d3097fb54bd2487348d9 to your computer and use it in GitHub Desktop.
Doctrine + DomainEvents
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
vendor/ | |
db.sqlite | |
composer.lock |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"require": { | |
"doctrine/orm": "@stable" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MyProject\Domain; | |
use Doctrine\ORM\Mapping as ORM; | |
use MyProject\DomainSuperTypes\AggregateRoot; | |
/** | |
* @Entity | |
*/ | |
class InventoryItem extends AggregateRoot | |
{ | |
/** | |
* @Id @GeneratedValue @Column(type="integer") | |
*/ | |
private $id; | |
/** | |
* @Column | |
*/ | |
private $name; | |
/** | |
* @Column(type="integer") | |
*/ | |
private $counter = 0; | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
$this->raise('InventoryItemCreated', array('name' => $name)); | |
} | |
public function rename($name) | |
{ | |
$this->name = $name; | |
$this->raise('InventoryItemRenamed', array('name' => $name)); | |
} | |
public function checkIn($count) | |
{ | |
$this->counter += $count; | |
$this->raise('ItemsCheckedIntoInventory', array('count' => $count)); | |
} | |
public function remove($count) | |
{ | |
$this->counter -= $count; | |
$this->raise('ItemsRemovedFromInventory', array('count' => $count)); | |
} | |
} | |
class EchoInventoryListener | |
{ | |
public function onInventoryItemCreated($event) | |
{ | |
printf("New item created with name %s\n", $event->name); | |
} | |
public function onInventoryItemRenamed($event) | |
{ | |
printf("Item was renamed to %s\n", $event->name); | |
} | |
public function onItemsCheckedIntoInventory($event) | |
{ | |
printf("There were %d new items checked into inventory\n", $event->count); | |
} | |
public function onItemsRemovedFromInventory($event) | |
{ | |
printf("There were %d items removed from inventory\n", $event->count); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Doctrine\ORM\Tools\Setup; | |
use Doctrine\ORM\Tools\SchemaTool; | |
use Doctrine\ORM\EntityManager; | |
use Doctrine\Common\EventManager; | |
use MyProject\DomainEvents\DirectEventDispatcher; | |
use MyProject\DomainEvents\DomainEventListener; | |
use MyProject\Domain\InventoryItem; | |
use MyProject\Domain\EchoInventoryListener; | |
require_once __DIR__ . "/vendor/autoload.php"; | |
require_once "DomainEventDispatcher.php"; | |
require_once "DomainSuperTypes.php"; | |
require_once "Domain.php"; | |
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__)); | |
$conn = array( | |
'driver' => 'pdo_sqlite', | |
'path' => __DIR__ . '/db.sqlite', | |
); | |
$evm = new EventManager(); | |
$evm->addEventListener( | |
array('postInsert', 'postUpdate', 'postRemove', 'postFlush'), | |
new DomainEventListener() | |
); | |
$evm->addEventListener( | |
array('onInventoryItemCreated', 'onInventoryItemRenamed', 'onItemsRemovedFromInventory', 'onItemsCheckedIntoInventory'), | |
new EchoInventoryListener | |
); | |
$entityManager = EntityManager::create($conn, $config, $evm); | |
$schemaTool = new SchemaTool($entityManager); | |
try { | |
$schemaTool->createSchema(array($entityManager->getClassMetadata(InventoryItem::CLASS))); | |
} catch(\Exception $e) { | |
} | |
$item = new InventoryItem('Cookies'); | |
$item->checkIn(10); | |
$entityManager->persist($item); | |
$entityManager->flush(); | |
$item->rename('Chocolate Cookies'); | |
$item->remove(5); | |
$entityManager->flush(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MyProject\DomainEvents; | |
use Doctrine\ORM\EntityManager; | |
use MyProject\DomainSuperTypes\AggregateRoot; | |
class DomainEventListener | |
{ | |
private $entities = array(); | |
public function postPersist($event) | |
{ | |
$this->keepAggregateRoots($event); | |
} | |
public function postUpdate($event) | |
{ | |
$this->keepAggregateRoots($event); | |
} | |
public function postRemove($event) | |
{ | |
$this->keepAggregateRoots($event); | |
} | |
public function postFlush($event) | |
{ | |
$entityManager = $event->getEntityManager(); | |
$evm = $entityManager->getEventManager(); | |
foreach ($this->entities as $entity) { | |
$class = $entityManager->getClassMetadata(get_class($entity)); | |
foreach ($entity->popEvents() as $event) { | |
$event->setAggregate($class->name, $class->getSingleIdReflectionProperty()->getValue($entity)); | |
$evm->dispatchEvent("on" . $event->getName(), $event); | |
} | |
} | |
$this->entities = array(); | |
} | |
private function keepAggregateRoots($event) | |
{ | |
$entity = $event->getEntity(); | |
if (!($entity instanceof AggregateRoot)) { | |
return; | |
} | |
$this->entities[] = $entity; | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MyProject\DomainSuperTypes; | |
use Doctrine\Common\EventArgs; | |
abstract class AggregateRoot | |
{ | |
private $events = array(); | |
public function popEvents() | |
{ | |
$events = $this->events; | |
$this->events = array(); | |
return $events; | |
} | |
protected function raise($eventName, array $properties) | |
{ | |
$this->events[] = new DomainEvent($eventName, $properties); | |
} | |
} | |
class DomainEvent extends EventArgs | |
{ | |
private $eventName; | |
private $properties; | |
private $aggregateClass; | |
private $aggregateId; | |
private $time; | |
public function __construct($eventName, array $properties) | |
{ | |
$this->eventName = $eventName; | |
$this->properties = $properties; | |
$this->time = microtime(true); | |
} | |
public function getTime() | |
{ | |
return $this->time; | |
} | |
public function getName() | |
{ | |
return $this->eventName; | |
} | |
public function __get($name) | |
{ | |
if (!isset($this->properties[$name])) { | |
throw new \RuntimeException("Property '" . $name . "' does not exist on event '" . $this->eventName); | |
} | |
return $this->properties[$name]; | |
} | |
public function setAggregate($class, $id) | |
{ | |
$this->aggregateClass = $class; | |
$this->aggregateId = $id; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment