├── .gitignore ├── .travis.yml ├── phpunit.xml.dist ├── tests └── Dflydev │ └── EventStore │ └── Doctrine │ └── Dbal │ ├── TestDomainEvent.php │ ├── DbalEventStoreTest.php │ └── DbalFollowStoreTest.php ├── src └── Dflydev │ └── EventStore │ └── Doctrine │ └── Dbal │ ├── TransactionalTableLockable.php │ ├── Schema │ ├── DbalFollowStoreSchemaUtil.php │ └── DbalEventStoreSchemaUtil.php │ ├── DbalFollowStore.php │ └── DbalEventStore.php ├── composer.json └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | phpunit.xml 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.5 5 | - 5.6 6 | - hhvm 7 | 8 | before_script: 9 | - composer install --prefer-source 10 | - mysql -e 'create database dflydev_event_store;' 11 | 12 | script: 13 | - vendor/bin/phpunit 14 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/Dflydev/EventStore/Doctrine/Dbal/TestDomainEvent.php: -------------------------------------------------------------------------------- 1 | value = $value; 16 | } 17 | 18 | public function value() 19 | { 20 | return $this->value; 21 | } 22 | 23 | public function jsonSerialize() 24 | { 25 | return ['contrived', $this->value]; 26 | } 27 | 28 | public static function jsonDeserialize($data) 29 | { 30 | if ('contrived' !== $data[0]) { 31 | throw new \RuntimeException('Invalid JSON serialization format detected'); 32 | } 33 | 34 | return new static($data[1]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Dflydev/EventStore/Doctrine/Dbal/TransactionalTableLockable.php: -------------------------------------------------------------------------------- 1 | transactional(function ($connection) use ($followStoreId, $tableName, $callback) { 12 | $connection->exec('LOCK TABLES '.$tableName.' WRITE'); 13 | 14 | $caughtException = null; 15 | 16 | try { 17 | $callback($connection, $followStoreId, $tableName); 18 | } catch (\Exception $e) { 19 | $caughtException = $e; 20 | } 21 | 22 | $connection->exec('UNLOCK TABLES'); 23 | 24 | if ($caughtException) { 25 | throw $caughtException; 26 | } 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dflydev/event-store-doctrine-dbal", 3 | "description": "Doctrine DBAL implementation of a naive event store.", 4 | "keywords": ["domain-driven design", "ddd", "cqrs", "event sourcing", "event store", "doctrine", "dbal"], 5 | "require": { 6 | "php": ">=5.5", 7 | "dflydev/event-store": "~0.0@dev", 8 | "doctrine/dbal": "~2.4", 9 | "event-centric/domain-events": "~0.0@dev", 10 | "event-centric/when": "~0.0@dev" 11 | }, 12 | "require-dev": { 13 | "phpunit/phpunit": "~3.7" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "Dflydev\\EventStore\\Doctrine\\Dbal\\": "src/Dflydev/EventStore/Doctrine/Dbal" 18 | } 19 | }, 20 | "autoload-dev": { 21 | "psr-4": { 22 | "Dflydev\\EventStore\\Doctrine\\Dbal\\": "tests/Dflydev/EventStore/Doctrine/Dbal" 23 | } 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "0.0.x-dev" 28 | } 29 | } 30 | 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/Dflydev/EventStore/Doctrine/Dbal/Schema/DbalFollowStoreSchemaUtil.php: -------------------------------------------------------------------------------- 1 | getSchemaManager(); 15 | 16 | $fromSchema = $schemaManager->createSchema(); 17 | $toSchema = static::getSchema($tableName); 18 | 19 | $comparator = new Comparator(); 20 | $diff = $comparator->compare($fromSchema, $toSchema); 21 | 22 | if ($sqlList = $diff->toSaveSql($connection->getDatabasePlatform())) { 23 | foreach ($sqlList as $sql) { 24 | $connection->exec($sql); 25 | } 26 | } 27 | } 28 | 29 | protected static function getSchema($tableName) 30 | { 31 | $schema = new Schema(); 32 | 33 | $dispatcherLastEvent = $schema->createTable($tableName); 34 | $dispatcherLastEvent->addColumn('event_id', 'integer'); 35 | $dispatcherLastEvent->addColumn('follow_store_id', 'string'); 36 | $dispatcherLastEvent->setPrimaryKey(['event_id', 'follow_store_id']); 37 | 38 | return $schema; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Dflydev/EventStore/Doctrine/Dbal/Schema/DbalEventStoreSchemaUtil.php: -------------------------------------------------------------------------------- 1 | getSchemaManager(); 15 | 16 | $fromSchema = $schemaManager->createSchema(); 17 | $toSchema = static::getSchema(); 18 | 19 | $comparator = new Comparator(); 20 | $diff = $comparator->compare($fromSchema, $toSchema); 21 | 22 | if ($sqlList = $diff->toSaveSql($connection->getDatabasePlatform())) { 23 | foreach ($sqlList as $sql) { 24 | $connection->exec($sql); 25 | } 26 | } 27 | } 28 | 29 | protected static function getSchema() 30 | { 31 | $schema = new Schema(); 32 | 33 | $esEventStore = $schema->createTable('dflydev_es_event_store'); 34 | $esEventStore->addColumn('event_id', 'integer', ['autoincrement' => true,]); 35 | $esEventStore->addColumn('event_body', 'blob'); 36 | $esEventStore->addColumn('event_type', 'string'); 37 | $esEventStore->addColumn('stream_name', 'string'); 38 | $esEventStore->addColumn('stream_version', 'integer'); 39 | $esEventStore->addIndex(['stream_name']); 40 | $esEventStore->addUniqueIndex(['stream_name', 'stream_version']); 41 | $esEventStore->setPrimaryKey(['event_id']); 42 | 43 | return $schema; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Dflydev/EventStore/Doctrine/Dbal/DbalFollowStore.php: -------------------------------------------------------------------------------- 1 | eventStore = $eventStore; 26 | $this->followStoreDispatcher = $followStoreDispatcher; 27 | $this->connection = $connection; 28 | $this->followStoreId = $followStoreId ?: 'default'; 29 | $this->tableName = $tableName ?: 'dflydev_fs_last_event'; 30 | } 31 | 32 | public function notifyDispatchableEvents() 33 | { 34 | $this->transactional($this->connection, $this->followStoreId, $this->tableName, function (Connection $connection, $followStoreId, $tableName) { 35 | $this->findAndDispatchNewDispatchableEvents($connection, $followStoreId, $tableName); 36 | }); 37 | } 38 | 39 | protected function transactional(Connection $connection, $followStoreId, $tableName, $callback) 40 | { 41 | $connection->transactional(function (Connection $connection) use ($followStoreId, $tableName, $callback) { 42 | $callback($connection, $followStoreId, $tableName); 43 | }); 44 | } 45 | 46 | private function findAndDispatchNewDispatchableEvents(Connection $connection, $followStoreId, $tableName) 47 | { 48 | $currentLastDispatchedEventId = $this->queryLastDispatchedEventId($connection, $followStoreId, $tableName); 49 | 50 | $lastDispatchedEventId = $this->followStoreDispatcher->notifyEventDispatchers( 51 | $this->eventStore, 52 | $currentLastDispatchedEventId, 53 | $this->eventDispatchers() 54 | ); 55 | 56 | if ($currentLastDispatchedEventId !== $lastDispatchedEventId) { 57 | $this->saveLastDispatchedEventId($connection, $followStoreId, $tableName, $lastDispatchedEventId); 58 | } 59 | } 60 | 61 | protected function saveLastDispatchedEventId(Connection $connection, $followStoreId, $tableName, $eventId) 62 | { 63 | $numberOfAffectedRows = $connection->executeUpdate( 64 | 'UPDATE '.$tableName.' SET event_id = ? WHERE follow_store_id = ?', 65 | [$eventId, $followStoreId] 66 | ); 67 | 68 | if ($numberOfAffectedRows < 1) { 69 | $numberOfAffectedRows = $connection->insert($tableName, [ 70 | 'event_id' => $eventId, 71 | 'follow_store_id' => $followStoreId, 72 | ]); 73 | } 74 | 75 | if ($numberOfAffectedRows < 1) { 76 | throw new \RuntimeException("Could not save last dispatched event ID"); 77 | } 78 | } 79 | 80 | protected function queryLastDispatchedEventId(Connection $connection, $followStoreId, $tableName) 81 | { 82 | try { 83 | if ($val = $connection->fetchColumn( 84 | 'SELECT MAX(event_id) FROM '.$tableName. ' WHERE follow_store_id = ?', 85 | [$followStoreId] 86 | )) { 87 | return (int) $val; 88 | } 89 | } catch (\Exception $e) 90 | { 91 | throw new \RuntimeException("Could not find the last dispatched event ID"); 92 | } 93 | 94 | return 0; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Dflydev/EventStore/Doctrine/Dbal/DbalEventStore.php: -------------------------------------------------------------------------------- 1 | = ? 31 | ORDER BY stream_version 32 | "; 33 | 34 | const EVENTS_SINCE_SQL = " 35 | SELECT event_id, 36 | event_body, 37 | event_type 38 | FROM dflydev_es_event_store 39 | WHERE event_id > ? 40 | ORDER BY event_id 41 | "; 42 | 43 | public function __construct( 44 | EventSerializer $eventSerializer, 45 | Connection $connection, 46 | EventNotifiable $eventNotifiable = null 47 | ) { 48 | $this->eventSerializer = $eventSerializer; 49 | $this->connection = $connection; 50 | $this->eventNotifiable = $eventNotifiable ?: new NullEventNotifiable(); 51 | } 52 | 53 | public function appendWith(EventStreamId $eventStreamId, DomainEvents $domainEvents) 54 | { 55 | try { 56 | $this->connection->transactional(function ($connection) use ($eventStreamId, $domainEvents) { 57 | $index = 0; 58 | 59 | foreach ($domainEvents as $domainEvent) { 60 | $this->appendEventStore($connection, $eventStreamId, $index++, $domainEvent); 61 | } 62 | 63 | $this->eventNotifiable->notifyDispatchableEvents(); 64 | }); 65 | } catch (\Exception $e) { 66 | throw new \RuntimeException('Could not save domain events to this event stream likely due to a conflict.'); 67 | } 68 | } 69 | 70 | public function eventStreamSince(EventStreamId $eventStreamId) 71 | { 72 | $result = $this->connection->fetchAll( 73 | self::EVENT_STREAM_SINCE_SQL, 74 | [$eventStreamId->streamName(), $eventStreamId->streamVersion()] 75 | ); 76 | 77 | return $this->buildEventStream($result); 78 | } 79 | 80 | public function fullEventStreamFor(EventStreamId $eventStreamId) 81 | { 82 | } 83 | 84 | public function eventsSince($lastReceivedEventId) 85 | { 86 | $result = $this->connection->fetchAll( 87 | self::EVENTS_SINCE_SQL, 88 | [$lastReceivedEventId] 89 | ); 90 | 91 | return $this->buildEventSequence($result); 92 | } 93 | 94 | private function appendEventStore( 95 | Connection $connection, 96 | EventSTreamId $identity, 97 | $index, 98 | DomainEvent $domainEvent 99 | ) { 100 | $connection->insert('dflydev_es_event_store', array( 101 | 'event_body' => $this->eventSerializer->serialize($domainEvent), 102 | 'event_type' => ClassFunctions::fqcn($domainEvent), 103 | 'stream_name' => (string) $identity->streamName(), 104 | 'stream_version' => $identity->streamVersion() + $index, 105 | )); 106 | } 107 | 108 | private function buildEventStream($result) 109 | { 110 | $events = []; 111 | $version = 0; 112 | 113 | foreach ($result as $storedEvent) { 114 | $version = $storedEvent['stream_version']; 115 | $className = $storedEvent['event_type']; 116 | $events[] = $className::jsonDeserialize(json_decode($storedEvent['event_body'])); 117 | } 118 | 119 | return new DefaultEventStream(new DomainEventsArray($events), $version); 120 | } 121 | 122 | private function buildEventSequence($result) 123 | { 124 | $events = []; 125 | 126 | foreach ($result as $storedEvent) { 127 | $className = $storedEvent['event_type']; 128 | $events[] = new DispatchableDomainEvent( 129 | $storedEvent['event_id'], 130 | $this->eventSerializer->deserialize($storedEvent['event_body'], $className) 131 | ); 132 | } 133 | 134 | return $events; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/Dflydev/EventStore/Doctrine/Dbal/DbalEventStoreTest.php: -------------------------------------------------------------------------------- 1 | configuration = new \Doctrine\DBAL\Configuration(); 23 | $this->connection = \Doctrine\DBAL\DriverManager::getConnection([ 24 | 'memory' => true, 25 | 'driver' => 'pdo_sqlite', 26 | ], $this->configuration); 27 | 28 | \Dflydev\EventStore\Doctrine\Dbal\Schema\DbalEventStoreSchemaUtil::updateSchema($this->connection); 29 | 30 | $this->testEventSerializer = new TestJsonEventSerializer(); 31 | $this->testEventNotifiable = new TestCountingEventNotifiable(); 32 | } 33 | 34 | /** @test */ 35 | public function shouldAppendEvents() 36 | { 37 | $eventStore = new DbalEventStore( 38 | $this->testEventSerializer, 39 | $this->connection, 40 | $this->testEventNotifiable 41 | ); 42 | 43 | $eventStreamId = EventStreamId::create('test'); 44 | 45 | $domainEvents = new DomainEventsArray([ 46 | new TestDomainEvent('one'), 47 | new TestDomainEvent('two'), 48 | new TestDomainEvent('three'), 49 | ]); 50 | 51 | $eventStore->appendWith($eventStreamId, $domainEvents); 52 | 53 | $rows = $this->connection->fetchAll('SELECT * FROM dflydev_es_event_store'); 54 | 55 | $this->assertEquals(3, count($rows)); 56 | $this->assertEquals(1, $this->testEventNotifiable->count()); 57 | } 58 | 59 | /** @test */ 60 | public function shouldAppendAdditionalEvents() 61 | { 62 | $eventStore = new DbalEventStore( 63 | $this->testEventSerializer, 64 | $this->connection, 65 | $this->testEventNotifiable 66 | ); 67 | 68 | $eventStreamId = EventStreamId::create('test'); 69 | 70 | $domainEvents = new DomainEventsArray([ 71 | new TestDomainEvent('one'), 72 | new TestDomainEvent('two'), 73 | new TestDomainEvent('three'), 74 | ]); 75 | 76 | $eventStore->appendWith($eventStreamId, $domainEvents); 77 | 78 | $eventStreamId = EventStreamId::create('test', 4); 79 | 80 | $eventStore->appendWith($eventStreamId, $domainEvents); 81 | 82 | $rows = $this->connection->fetchAll('SELECT * FROM dflydev_es_event_store'); 83 | 84 | $this->assertEquals(6, count($rows)); 85 | $this->assertEquals(2, $this->testEventNotifiable->count()); 86 | } 87 | 88 | /** @test */ 89 | public function shouldAppendEventsWithDifferentNamesButSameIds() 90 | { 91 | $eventStore = new DbalEventStore( 92 | $this->testEventSerializer, 93 | $this->connection, 94 | $this->testEventNotifiable 95 | ); 96 | 97 | $eventStreamId = EventStreamId::create('test'); 98 | 99 | $domainEvents = new DomainEventsArray([ 100 | new TestDomainEvent('one'), 101 | new TestDomainEvent('two'), 102 | new TestDomainEvent('three'), 103 | ]); 104 | 105 | $eventStore->appendWith($eventStreamId, $domainEvents); 106 | 107 | $eventStreamId = EventStreamId::create('test2'); 108 | 109 | $domainEvents = new DomainEventsArray([ 110 | new TestDomainEvent('one'), 111 | new TestDomainEvent('two'), 112 | new TestDomainEvent('three'), 113 | ]); 114 | 115 | $eventStore->appendWith($eventStreamId, $domainEvents); 116 | 117 | $rows = $this->connection->fetchAll('SELECT * FROM dflydev_es_event_store'); 118 | 119 | $this->assertEquals(6, count($rows)); 120 | $this->assertEquals(2, $this->testEventNotifiable->count()); 121 | } 122 | 123 | /** 124 | * @expectedException RuntimeException 125 | * @expectedExceptionMessage conflict 126 | * @test 127 | */ 128 | public function shouldThrowExceptionWhenAppendingConflictingEvents() 129 | { 130 | $eventStore = new DbalEventStore( 131 | $this->testEventSerializer, 132 | $this->connection, 133 | $this->testEventNotifiable 134 | ); 135 | 136 | $eventStreamId = EventStreamId::create('test'); 137 | 138 | $domainEvents = new DomainEventsArray([ 139 | new TestDomainEvent('one'), 140 | new TestDomainEvent('two'), 141 | new TestDomainEvent('three'), 142 | ]); 143 | 144 | $eventStore->appendWith($eventStreamId, $domainEvents); 145 | $eventStore->appendWith($eventStreamId, $domainEvents); 146 | 147 | $rows = $this->connection->fetchAll('SELECT * FROM dflydev_es_event_store'); 148 | 149 | $this->assertEquals(3, count($rows)); 150 | $this->assertEquals(1, $this->testEventNotifiable->count()); 151 | } 152 | 153 | /** @test */ 154 | public function shouldReturnEventStream() 155 | { 156 | $eventStore = new DbalEventStore( 157 | $this->testEventSerializer, 158 | $this->connection, 159 | $this->testEventNotifiable 160 | ); 161 | 162 | $eventStreamId = EventStreamId::create('test'); 163 | 164 | $domainEvents = new DomainEventsArray([ 165 | new TestDomainEvent('one'), 166 | new TestDomainEvent('two'), 167 | new TestDomainEvent('three'), 168 | ]); 169 | 170 | $eventStore->appendWith($eventStreamId, $domainEvents); 171 | 172 | $eventStream = $eventStore->eventStreamSince($eventStreamId); 173 | 174 | $this->assertEquals(3, $eventStream->version()); 175 | $domainEvents = $eventStream->domainEvents(); 176 | $this->assertEquals('one', $domainEvents[0]->value()); 177 | $this->assertEquals('two', $domainEvents[1]->value()); 178 | $this->assertEquals('three', $domainEvents[2]->value()); 179 | } 180 | 181 | /** @test */ 182 | public function shouldReturnEventsSince() 183 | { 184 | $eventStore = new DbalEventStore( 185 | $this->testEventSerializer, 186 | $this->connection, 187 | $this->testEventNotifiable 188 | ); 189 | 190 | $eventStreamId = EventStreamId::create('test'); 191 | 192 | $domainEvents = new DomainEventsArray([ 193 | new TestDomainEvent('one'), 194 | new TestDomainEvent('two'), 195 | new TestDomainEvent('three'), 196 | ]); 197 | 198 | $eventStore->appendWith($eventStreamId, $domainEvents); 199 | 200 | $domainEvents = $eventStore->eventsSince(0); 201 | 202 | $this->assertEquals(3, count($domainEvents)); 203 | $this->assertEquals(1, $domainEvents[0]->eventId()); 204 | $this->assertEquals('one', $domainEvents[0]->domainEvent()->value()); 205 | $this->assertEquals(2, $domainEvents[1]->eventId()); 206 | $this->assertEquals('two', $domainEvents[1]->domainEvent()->value()); 207 | $this->assertEquals(3, $domainEvents[2]->eventId()); 208 | $this->assertEquals('three', $domainEvents[2]->domainEvent()->value()); 209 | 210 | $domainEvents = $eventStore->eventsSince(2); 211 | 212 | $this->assertEquals(1, count($domainEvents)); 213 | $this->assertEquals(3, $domainEvents[0]->eventId()); 214 | $this->assertEquals('three', $domainEvents[0]->domainEvent()->value()); 215 | 216 | $domainEvents = $eventStore->eventsSince(3); 217 | 218 | $this->assertEquals(0, count($domainEvents)); 219 | } 220 | } 221 | 222 | class TestJsonEventSerializer extends JsonEventSerializer 223 | { 224 | public function deserialize($data, $className) 225 | { 226 | return $className::jsonDeserialize(json_decode($data, true)); 227 | } 228 | } 229 | 230 | class TestCountingEventNotifiable implements EventNotifiable 231 | { 232 | private $count = 0; 233 | 234 | public function notifyDispatchableEvents() 235 | { 236 | $this->count++; 237 | } 238 | 239 | public function count() 240 | { 241 | return $this->count; 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /tests/Dflydev/EventStore/Doctrine/Dbal/DbalFollowStoreTest.php: -------------------------------------------------------------------------------- 1 | configuration = new \Doctrine\DBAL\Configuration(); 31 | $this->connection = \Doctrine\DBAL\DriverManager::getConnection([ 32 | 'dbname' => $_ENV['testsuite_mysql_db_name'], 33 | 'user' => $_ENV['testsuite_mysql_db_user'], 34 | 'password' => $_ENV['testsuite_mysql_db_password'], 35 | 'host' => $_ENV['testsuite_mysql_db_host'], 36 | 'driver' => 'pdo_mysql', 37 | ], $this->configuration); 38 | 39 | $this->connection->executeUpdate('DROP TABLE IF EXISTS test___dflydev_event_store'); 40 | 41 | \Dflydev\EventStore\Doctrine\Dbal\Schema\DbalFollowStoreSchemaUtil::updateSchema($this->connection, 'test___dflydev_event_store'); 42 | } 43 | 44 | /** @test */ 45 | public function shouldNotifyDispatchableEvents() 46 | { 47 | $eventStore = new TestEventStore([ 48 | [0, [ 49 | new DispatchableDomainEvent(1, new TestDomainEvent('one')), 50 | new DispatchableDomainEvent(2, new TestDomainEvent('two')), 51 | ]], 52 | [2, [ 53 | new DispatchableDomainEvent(3, new TestDomainEvent('three')), 54 | ]], 55 | [3, [ 56 | new DispatchableDomainEvent(4, new TestDomainEvent('AAA')), 57 | new DispatchableDomainEvent(5, new TestDomainEvent('BBB')), 58 | new DispatchableDomainEvent(6, new TestDomainEvent('CCC')), 59 | new DispatchableDomainEvent(7, new TestDomainEvent('DDD')), 60 | ]], 61 | [7, []], 62 | [7, []], 63 | [7, [ 64 | new DispatchableDomainEvent(8, new TestDomainEvent('eee')), 65 | ]], 66 | ]); 67 | 68 | $followStoreDispatcher = new FollowStoreDispatcher(); 69 | 70 | $eventDispatcher = function () { 71 | $args = func_get_args(); 72 | 73 | return new TestEventDispatcher($args); 74 | }; 75 | 76 | /** @var TestEventDispatcher $testEventDispatcher */ 77 | $testEventDispatcher = $eventDispatcher('one', 'two', 'three', 'AAA', 'BBB', 'CCC', 'DDD', 'eee'); 78 | 79 | $followStore = new DbalFollowStore( 80 | $eventStore, 81 | $followStoreDispatcher, 82 | $this->connection, 83 | null, 84 | 'test___dflydev_event_store' 85 | ); 86 | 87 | $followStore->registerEventDispatcher($testEventDispatcher); 88 | 89 | $followStore->notifyDispatchableEvents(); 90 | 91 | $this->assertEquals(2, $testEventDispatcher->numberOfDomainEventsDispatched()); 92 | 93 | $followStore->notifyDispatchableEvents(); 94 | 95 | $this->assertEquals(3, $testEventDispatcher->numberOfDomainEventsDispatched()); 96 | 97 | $followStore->notifyDispatchableEvents(); 98 | 99 | $this->assertEquals(7, $testEventDispatcher->numberOfDomainEventsDispatched()); 100 | 101 | $followStore = new DbalFollowStore( 102 | $eventStore, 103 | $followStoreDispatcher, 104 | $this->connection, 105 | null, 106 | 'test___dflydev_event_store' 107 | ); 108 | 109 | $followStore->registerEventDispatcher($testEventDispatcher); 110 | 111 | $followStore->notifyDispatchableEvents(); 112 | 113 | $followStore->notifyDispatchableEvents(); 114 | 115 | $followStore->notifyDispatchableEvents(); 116 | 117 | $this->assertEquals(8, $testEventDispatcher->numberOfDomainEventsDispatched()); 118 | } 119 | 120 | /** @test */ 121 | public function shouldNotifyDispatchableEventsSplit() 122 | { 123 | $eventStore = new TestEventStore([ 124 | [0, [ 125 | new DispatchableDomainEvent(1, new TestDomainEvent('one')), 126 | new DispatchableDomainEvent(2, new TestDomainEvent('two')), 127 | ]], 128 | [2, [ 129 | new DispatchableDomainEvent(3, new TestDomainEvent('three')), 130 | ]], 131 | [3, [ 132 | new DispatchableDomainEvent(4, new TestDomainEvent('AAA')), 133 | new DispatchableDomainEvent(5, new TestDomainEvent('BBB')), 134 | new DispatchableDomainEvent(6, new TestDomainEvent('CCC')), 135 | new DispatchableDomainEvent(7, new TestDomainEvent('DDD')), 136 | ]], 137 | [7, []], 138 | [7, []], 139 | [7, [ 140 | new DispatchableDomainEvent(8, new TestDomainEvent('eee')), 141 | ]], 142 | 143 | [0, [ 144 | new DispatchableDomainEvent(1, new TestDomainEvent('one')), 145 | new DispatchableDomainEvent(2, new TestDomainEvent('two')), 146 | ]], 147 | [2, [ 148 | new DispatchableDomainEvent(3, new TestDomainEvent('three')), 149 | ]], 150 | [3, [ 151 | new DispatchableDomainEvent(4, new TestDomainEvent('AAA')), 152 | new DispatchableDomainEvent(5, new TestDomainEvent('BBB')), 153 | new DispatchableDomainEvent(6, new TestDomainEvent('CCC')), 154 | new DispatchableDomainEvent(7, new TestDomainEvent('DDD')), 155 | ]], 156 | [7, []], 157 | [7, []], 158 | [7, [ 159 | new DispatchableDomainEvent(8, new TestDomainEvent('eee')), 160 | ]], 161 | ]); 162 | 163 | $followStoreDispatcher = new FollowStoreDispatcher(); 164 | 165 | $eventDispatcher = function () { 166 | $args = func_get_args(); 167 | 168 | return new TestEventDispatcher($args); 169 | }; 170 | 171 | /** @var TestEventDispatcher $testEventDispatcher */ 172 | $testEventDispatcher = $eventDispatcher( 173 | 'one', 'two', 'three', 'AAA', 'BBB', 'CCC', 'DDD', 'eee', 174 | 'one', 'two', 'three', 'AAA', 'BBB', 'CCC', 'DDD', 'eee' 175 | ); 176 | 177 | $offset = 0; 178 | foreach (['test000', 'test001'] as $followStoreId) { 179 | $followStore = new DbalFollowStore( 180 | $eventStore, 181 | $followStoreDispatcher, 182 | $this->connection, 183 | $followStoreId, 184 | 'test___dflydev_event_store' 185 | ); 186 | 187 | $followStore->registerEventDispatcher($testEventDispatcher); 188 | 189 | $followStore->notifyDispatchableEvents(); 190 | 191 | $this->assertEquals($offset + 2, $testEventDispatcher->numberOfDomainEventsDispatched()); 192 | 193 | $followStore->notifyDispatchableEvents(); 194 | 195 | $this->assertEquals($offset + 3, $testEventDispatcher->numberOfDomainEventsDispatched()); 196 | 197 | $followStore->notifyDispatchableEvents(); 198 | 199 | $this->assertEquals($offset + 7, $testEventDispatcher->numberOfDomainEventsDispatched()); 200 | 201 | $followStore = new DbalFollowStore( 202 | $eventStore, 203 | $followStoreDispatcher, 204 | $this->connection, 205 | $followStoreId, 206 | 'test___dflydev_event_store' 207 | ); 208 | 209 | $followStore->registerEventDispatcher($testEventDispatcher); 210 | 211 | $followStore->notifyDispatchableEvents(); 212 | 213 | $followStore->notifyDispatchableEvents(); 214 | 215 | $followStore->notifyDispatchableEvents(); 216 | 217 | $this->assertEquals($offset + 8, $testEventDispatcher->numberOfDomainEventsDispatched()); 218 | 219 | $offset += 8; 220 | } 221 | 222 | $rows = $this->connection->fetchAll('SELECT * FROM test___dflydev_event_store'); 223 | 224 | $this->assertEquals(2, count($rows)); 225 | } 226 | } 227 | 228 | class TestEventStore implements EventStore 229 | { 230 | private $eventsSinceResponses = []; 231 | private $eventsSinceResponsesIndex = 0; 232 | 233 | public function __construct(array $eventsSinceResponses = []) 234 | { 235 | $this->eventsSinceResponses = $eventsSinceResponses; 236 | } 237 | public function appendWith(EventStreamId $eventStreamId, DomainEvents $domainEvents) 238 | { 239 | throw new \RuntimeException("Not implemented."); 240 | } 241 | 242 | public function eventStreamSince(EventStreamId $eventStreamId) 243 | { 244 | throw new \RuntimeException("Not implemented."); 245 | } 246 | 247 | public function fullEventStreamFor(EventStreamId $eventStreamId) 248 | { 249 | throw new \RuntimeException("Not implemented."); 250 | } 251 | 252 | public function eventsSince($lastReceivedEventId) 253 | { 254 | $expected = $this->eventsSinceResponses[$this->eventsSinceResponsesIndex++]; 255 | 256 | if ($lastReceivedEventId !== $expected[0]) { 257 | throw new \RuntimeException("eventsSince received '".$lastReceivedEventId."' but expected '".$expected[0]."'"); 258 | } 259 | 260 | return $expected[1]; 261 | } 262 | } 263 | 264 | class TestEventDispatcher implements EventDispatcher 265 | { 266 | private $expectedValues = []; 267 | private $expectedValuesIndex = 0; 268 | 269 | public function __construct(array $expectedValues = []) 270 | { 271 | $this->expectedValues = $expectedValues; 272 | } 273 | 274 | public function dispatch(DispatchableDomainEvent $dispatchableDomainEvent) 275 | { 276 | $expected = $this->expectedValues[$this->expectedValuesIndex++]; 277 | $actual = $dispatchableDomainEvent->domainEvent()->value(); 278 | if ($actual !== $expected) { 279 | throw new \RuntimeException("dispatch received '".$actual."' but expected '".$expected."'"); 280 | } 281 | } 282 | 283 | public function numberOfDomainEventsDispatched() 284 | { 285 | return $this->expectedValuesIndex; 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "d6ce62bfe1b6b15810380b8998dcab37", 8 | "packages": [ 9 | { 10 | "name": "dflydev/event-store", 11 | "version": "dev-master", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/dflydev/dflydev-event-store.git", 15 | "reference": "e87bf2e3be110d6672c606c1413525748091d835" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/dflydev/dflydev-event-store/zipball/e87bf2e3be110d6672c606c1413525748091d835", 20 | "reference": "e87bf2e3be110d6672c606c1413525748091d835", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "event-centric/domain-events": "~0.0@dev", 25 | "event-centric/when": "~0.0@dev", 26 | "php": ">=5.5" 27 | }, 28 | "type": "library", 29 | "extra": { 30 | "branch-alias": { 31 | "dev-master": "0.0.x-dev" 32 | } 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Dflydev\\EventStore\\": "src/Dflydev/EventStore" 37 | } 38 | }, 39 | "description": "Naive event store.", 40 | "keywords": [ 41 | "cqrs", 42 | "ddd", 43 | "domain-driven design", 44 | "event sourcing", 45 | "event store" 46 | ], 47 | "support": { 48 | "source": "https://github.com/dflydev/dflydev-event-store/tree/master", 49 | "issues": "https://github.com/dflydev/dflydev-event-store/issues" 50 | }, 51 | "time": "2014-07-10 22:22:48" 52 | }, 53 | { 54 | "name": "doctrine/annotations", 55 | "version": "v1.2.0", 56 | "source": { 57 | "type": "git", 58 | "url": "https://github.com/doctrine/annotations.git", 59 | "reference": "d9b1a37e9351ddde1f19f09a02e3d6ee92e82efd" 60 | }, 61 | "dist": { 62 | "type": "zip", 63 | "url": "https://api.github.com/repos/doctrine/annotations/zipball/d9b1a37e9351ddde1f19f09a02e3d6ee92e82efd", 64 | "reference": "d9b1a37e9351ddde1f19f09a02e3d6ee92e82efd", 65 | "shasum": "" 66 | }, 67 | "require": { 68 | "doctrine/lexer": "1.*", 69 | "php": ">=5.3.2" 70 | }, 71 | "require-dev": { 72 | "doctrine/cache": "1.*", 73 | "phpunit/phpunit": "4.*" 74 | }, 75 | "type": "library", 76 | "extra": { 77 | "branch-alias": { 78 | "dev-master": "1.3.x-dev" 79 | } 80 | }, 81 | "autoload": { 82 | "psr-0": { 83 | "Doctrine\\Common\\Annotations\\": "lib/" 84 | } 85 | }, 86 | "notification-url": "https://packagist.org/downloads/", 87 | "license": [ 88 | "MIT" 89 | ], 90 | "authors": [ 91 | { 92 | "name": "Jonathan Wage", 93 | "email": "jonwage@gmail.com", 94 | "homepage": "http://www.jwage.com/", 95 | "role": "Creator" 96 | }, 97 | { 98 | "name": "Guilherme Blanco", 99 | "email": "guilhermeblanco@gmail.com", 100 | "homepage": "http://www.instaclick.com" 101 | }, 102 | { 103 | "name": "Roman Borschel", 104 | "email": "roman@code-factory.org" 105 | }, 106 | { 107 | "name": "Benjamin Eberlei", 108 | "email": "kontakt@beberlei.de" 109 | }, 110 | { 111 | "name": "Johannes Schmitt", 112 | "email": "schmittjoh@gmail.com", 113 | "homepage": "http://jmsyst.com", 114 | "role": "Developer of wrapped JMSSerializerBundle" 115 | } 116 | ], 117 | "description": "Docblock Annotations Parser", 118 | "homepage": "http://www.doctrine-project.org", 119 | "keywords": [ 120 | "annotations", 121 | "docblock", 122 | "parser" 123 | ], 124 | "time": "2014-07-06 15:52:21" 125 | }, 126 | { 127 | "name": "doctrine/cache", 128 | "version": "v1.3.0", 129 | "source": { 130 | "type": "git", 131 | "url": "https://github.com/doctrine/cache.git", 132 | "reference": "e16d7adf45664a50fa86f515b6d5e7f670130449" 133 | }, 134 | "dist": { 135 | "type": "zip", 136 | "url": "https://api.github.com/repos/doctrine/cache/zipball/e16d7adf45664a50fa86f515b6d5e7f670130449", 137 | "reference": "e16d7adf45664a50fa86f515b6d5e7f670130449", 138 | "shasum": "" 139 | }, 140 | "require": { 141 | "php": ">=5.3.2" 142 | }, 143 | "conflict": { 144 | "doctrine/common": ">2.2,<2.4" 145 | }, 146 | "require-dev": { 147 | "phpunit/phpunit": ">=3.7", 148 | "satooshi/php-coveralls": "~0.6" 149 | }, 150 | "type": "library", 151 | "extra": { 152 | "branch-alias": { 153 | "dev-master": "1.0.x-dev" 154 | } 155 | }, 156 | "autoload": { 157 | "psr-0": { 158 | "Doctrine\\Common\\Cache\\": "lib/" 159 | } 160 | }, 161 | "notification-url": "https://packagist.org/downloads/", 162 | "license": [ 163 | "MIT" 164 | ], 165 | "authors": [ 166 | { 167 | "name": "Jonathan Wage", 168 | "email": "jonwage@gmail.com", 169 | "homepage": "http://www.jwage.com/", 170 | "role": "Creator" 171 | }, 172 | { 173 | "name": "Guilherme Blanco", 174 | "email": "guilhermeblanco@gmail.com", 175 | "homepage": "http://www.instaclick.com" 176 | }, 177 | { 178 | "name": "Roman Borschel", 179 | "email": "roman@code-factory.org" 180 | }, 181 | { 182 | "name": "Benjamin Eberlei", 183 | "email": "kontakt@beberlei.de" 184 | }, 185 | { 186 | "name": "Johannes Schmitt", 187 | "email": "schmittjoh@gmail.com", 188 | "homepage": "http://jmsyst.com", 189 | "role": "Developer of wrapped JMSSerializerBundle" 190 | } 191 | ], 192 | "description": "Caching library offering an object-oriented API for many cache backends", 193 | "homepage": "http://www.doctrine-project.org", 194 | "keywords": [ 195 | "cache", 196 | "caching" 197 | ], 198 | "time": "2013-10-25 19:04:14" 199 | }, 200 | { 201 | "name": "doctrine/collections", 202 | "version": "v1.2", 203 | "source": { 204 | "type": "git", 205 | "url": "https://github.com/doctrine/collections.git", 206 | "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2" 207 | }, 208 | "dist": { 209 | "type": "zip", 210 | "url": "https://api.github.com/repos/doctrine/collections/zipball/b99c5c46c87126201899afe88ec490a25eedd6a2", 211 | "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2", 212 | "shasum": "" 213 | }, 214 | "require": { 215 | "php": ">=5.3.2" 216 | }, 217 | "type": "library", 218 | "extra": { 219 | "branch-alias": { 220 | "dev-master": "1.2.x-dev" 221 | } 222 | }, 223 | "autoload": { 224 | "psr-0": { 225 | "Doctrine\\Common\\Collections\\": "lib/" 226 | } 227 | }, 228 | "notification-url": "https://packagist.org/downloads/", 229 | "license": [ 230 | "MIT" 231 | ], 232 | "authors": [ 233 | { 234 | "name": "Jonathan H. Wage", 235 | "email": "jonwage@gmail.com", 236 | "homepage": "http://www.jwage.com/", 237 | "role": "Creator" 238 | }, 239 | { 240 | "name": "Guilherme Blanco", 241 | "email": "guilhermeblanco@gmail.com", 242 | "homepage": "http://www.instaclick.com" 243 | }, 244 | { 245 | "name": "Roman Borschel", 246 | "email": "roman@code-factory.org" 247 | }, 248 | { 249 | "name": "Benjamin Eberlei", 250 | "email": "kontakt@beberlei.de" 251 | }, 252 | { 253 | "name": "Johannes Schmitt", 254 | "email": "schmittjoh@gmail.com", 255 | "homepage": "https://github.com/schmittjoh", 256 | "role": "Developer of wrapped JMSSerializerBundle" 257 | } 258 | ], 259 | "description": "Collections Abstraction library", 260 | "homepage": "http://www.doctrine-project.org", 261 | "keywords": [ 262 | "array", 263 | "collections", 264 | "iterator" 265 | ], 266 | "time": "2014-02-03 23:07:43" 267 | }, 268 | { 269 | "name": "doctrine/common", 270 | "version": "v2.4.2", 271 | "source": { 272 | "type": "git", 273 | "url": "https://github.com/doctrine/common.git", 274 | "reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b" 275 | }, 276 | "dist": { 277 | "type": "zip", 278 | "url": "https://api.github.com/repos/doctrine/common/zipball/5db6ab40e4c531f14dad4ca96a394dfce5d4255b", 279 | "reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b", 280 | "shasum": "" 281 | }, 282 | "require": { 283 | "doctrine/annotations": "1.*", 284 | "doctrine/cache": "1.*", 285 | "doctrine/collections": "1.*", 286 | "doctrine/inflector": "1.*", 287 | "doctrine/lexer": "1.*", 288 | "php": ">=5.3.2" 289 | }, 290 | "require-dev": { 291 | "phpunit/phpunit": "~3.7" 292 | }, 293 | "type": "library", 294 | "extra": { 295 | "branch-alias": { 296 | "dev-master": "2.4.x-dev" 297 | } 298 | }, 299 | "autoload": { 300 | "psr-0": { 301 | "Doctrine\\Common\\": "lib/" 302 | } 303 | }, 304 | "notification-url": "https://packagist.org/downloads/", 305 | "license": [ 306 | "MIT" 307 | ], 308 | "authors": [ 309 | { 310 | "name": "Jonathan Wage", 311 | "email": "jonwage@gmail.com", 312 | "homepage": "http://www.jwage.com/", 313 | "role": "Creator" 314 | }, 315 | { 316 | "name": "Guilherme Blanco", 317 | "email": "guilhermeblanco@gmail.com", 318 | "homepage": "http://www.instaclick.com" 319 | }, 320 | { 321 | "name": "Roman Borschel", 322 | "email": "roman@code-factory.org" 323 | }, 324 | { 325 | "name": "Benjamin Eberlei", 326 | "email": "kontakt@beberlei.de" 327 | }, 328 | { 329 | "name": "Johannes Schmitt", 330 | "email": "schmittjoh@gmail.com", 331 | "homepage": "http://jmsyst.com", 332 | "role": "Developer of wrapped JMSSerializerBundle" 333 | } 334 | ], 335 | "description": "Common Library for Doctrine projects", 336 | "homepage": "http://www.doctrine-project.org", 337 | "keywords": [ 338 | "annotations", 339 | "collections", 340 | "eventmanager", 341 | "persistence", 342 | "spl" 343 | ], 344 | "time": "2014-05-21 19:28:51" 345 | }, 346 | { 347 | "name": "doctrine/dbal", 348 | "version": "v2.4.2", 349 | "source": { 350 | "type": "git", 351 | "url": "https://github.com/doctrine/dbal.git", 352 | "reference": "fec965d330c958e175c39e61c3f6751955af32d0" 353 | }, 354 | "dist": { 355 | "type": "zip", 356 | "url": "https://api.github.com/repos/doctrine/dbal/zipball/fec965d330c958e175c39e61c3f6751955af32d0", 357 | "reference": "fec965d330c958e175c39e61c3f6751955af32d0", 358 | "shasum": "" 359 | }, 360 | "require": { 361 | "doctrine/common": "~2.4", 362 | "php": ">=5.3.2" 363 | }, 364 | "require-dev": { 365 | "phpunit/phpunit": "3.7.*", 366 | "symfony/console": "~2.0" 367 | }, 368 | "suggest": { 369 | "symfony/console": "Allows use of the command line interface" 370 | }, 371 | "type": "library", 372 | "autoload": { 373 | "psr-0": { 374 | "Doctrine\\DBAL\\": "lib/" 375 | } 376 | }, 377 | "notification-url": "https://packagist.org/downloads/", 378 | "license": [ 379 | "MIT" 380 | ], 381 | "authors": [ 382 | { 383 | "name": "Jonathan Wage", 384 | "email": "jonwage@gmail.com", 385 | "homepage": "http://www.jwage.com/", 386 | "role": "Creator" 387 | }, 388 | { 389 | "name": "Guilherme Blanco", 390 | "email": "guilhermeblanco@gmail.com", 391 | "homepage": "http://www.instaclick.com" 392 | }, 393 | { 394 | "name": "Roman Borschel", 395 | "email": "roman@code-factory.org" 396 | }, 397 | { 398 | "name": "Benjamin Eberlei", 399 | "email": "kontakt@beberlei.de" 400 | } 401 | ], 402 | "description": "Database Abstraction Layer", 403 | "homepage": "http://www.doctrine-project.org", 404 | "keywords": [ 405 | "database", 406 | "dbal", 407 | "persistence", 408 | "queryobject" 409 | ], 410 | "time": "2014-01-01 16:43:57" 411 | }, 412 | { 413 | "name": "doctrine/inflector", 414 | "version": "v1.0", 415 | "source": { 416 | "type": "git", 417 | "url": "https://github.com/doctrine/inflector.git", 418 | "reference": "54b8333d2a5682afdc690060c1cf384ba9f47f08" 419 | }, 420 | "dist": { 421 | "type": "zip", 422 | "url": "https://api.github.com/repos/doctrine/inflector/zipball/54b8333d2a5682afdc690060c1cf384ba9f47f08", 423 | "reference": "54b8333d2a5682afdc690060c1cf384ba9f47f08", 424 | "shasum": "" 425 | }, 426 | "require": { 427 | "php": ">=5.3.2" 428 | }, 429 | "type": "library", 430 | "autoload": { 431 | "psr-0": { 432 | "Doctrine\\Common\\Inflector\\": "lib/" 433 | } 434 | }, 435 | "notification-url": "https://packagist.org/downloads/", 436 | "license": [ 437 | "MIT" 438 | ], 439 | "authors": [ 440 | { 441 | "name": "Jonathan Wage", 442 | "email": "jonwage@gmail.com", 443 | "homepage": "http://www.jwage.com/", 444 | "role": "Creator" 445 | }, 446 | { 447 | "name": "Guilherme Blanco", 448 | "email": "guilhermeblanco@gmail.com", 449 | "homepage": "http://www.instaclick.com" 450 | }, 451 | { 452 | "name": "Roman Borschel", 453 | "email": "roman@code-factory.org" 454 | }, 455 | { 456 | "name": "Benjamin Eberlei", 457 | "email": "kontakt@beberlei.de" 458 | }, 459 | { 460 | "name": "Johannes Schmitt", 461 | "email": "schmittjoh@gmail.com", 462 | "homepage": "http://jmsyst.com", 463 | "role": "Developer of wrapped JMSSerializerBundle" 464 | } 465 | ], 466 | "description": "Common String Manipulations with regard to casing and singular/plural rules.", 467 | "homepage": "http://www.doctrine-project.org", 468 | "keywords": [ 469 | "inflection", 470 | "pluarlize", 471 | "singuarlize", 472 | "string" 473 | ], 474 | "time": "2013-01-10 21:49:15" 475 | }, 476 | { 477 | "name": "doctrine/lexer", 478 | "version": "v1.0", 479 | "source": { 480 | "type": "git", 481 | "url": "https://github.com/doctrine/lexer.git", 482 | "reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb" 483 | }, 484 | "dist": { 485 | "type": "zip", 486 | "url": "https://api.github.com/repos/doctrine/lexer/zipball/2f708a85bb3aab5d99dab8be435abd73e0b18acb", 487 | "reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb", 488 | "shasum": "" 489 | }, 490 | "require": { 491 | "php": ">=5.3.2" 492 | }, 493 | "type": "library", 494 | "autoload": { 495 | "psr-0": { 496 | "Doctrine\\Common\\Lexer\\": "lib/" 497 | } 498 | }, 499 | "notification-url": "https://packagist.org/downloads/", 500 | "license": [ 501 | "MIT" 502 | ], 503 | "authors": [ 504 | { 505 | "name": "Guilherme Blanco", 506 | "email": "guilhermeblanco@gmail.com", 507 | "homepage": "http://www.instaclick.com" 508 | }, 509 | { 510 | "name": "Roman Borschel", 511 | "email": "roman@code-factory.org" 512 | }, 513 | { 514 | "name": "Johannes Schmitt", 515 | "email": "schmittjoh@gmail.com", 516 | "homepage": "http://jmsyst.com", 517 | "role": "Developer of wrapped JMSSerializerBundle" 518 | } 519 | ], 520 | "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", 521 | "homepage": "http://www.doctrine-project.org", 522 | "keywords": [ 523 | "lexer", 524 | "parser" 525 | ], 526 | "time": "2013-01-12 18:59:04" 527 | }, 528 | { 529 | "name": "event-centric/domain-events", 530 | "version": "dev-master", 531 | "source": { 532 | "type": "git", 533 | "url": "https://github.com/event-centric/domain-events.git", 534 | "reference": "03fbc29f96dd85741b571c02ea0775a079a7b810" 535 | }, 536 | "dist": { 537 | "type": "zip", 538 | "url": "https://api.github.com/repos/event-centric/domain-events/zipball/03fbc29f96dd85741b571c02ea0775a079a7b810", 539 | "reference": "03fbc29f96dd85741b571c02ea0775a079a7b810", 540 | "shasum": "" 541 | }, 542 | "require": { 543 | "php": ">=5.5" 544 | }, 545 | "require-dev": { 546 | "phpunit/phpunit": "~4.1" 547 | }, 548 | "type": "library", 549 | "extra": { 550 | "branch-alias": { 551 | "dev-master": "0.0.x-dev" 552 | } 553 | }, 554 | "autoload": { 555 | "psr-4": { 556 | "EventCentric\\DomainEvents\\": [ 557 | "src/EventCentric/DomainEvents" 558 | ] 559 | } 560 | }, 561 | "autoload-dev": { 562 | "psr-4": { 563 | "EventCentric\\": [ 564 | "tests/EventCentric" 565 | ] 566 | } 567 | }, 568 | "license": [ 569 | "MIT" 570 | ], 571 | "authors": [ 572 | { 573 | "name": "Mathias Verraes", 574 | "email": "mathias@verraes.net", 575 | "homepage": "http://verraes.net" 576 | } 577 | ], 578 | "description": "Domain Events", 579 | "homepage": "https://github.com/event-centric/domain-events", 580 | "keywords": [ 581 | "cqrs", 582 | "ddd", 583 | "domain-driven design", 584 | "event sourcing", 585 | "reactive" 586 | ], 587 | "support": { 588 | "source": "https://github.com/event-centric/domain-events/tree/master", 589 | "issues": "https://github.com/event-centric/domain-events/issues" 590 | }, 591 | "time": "2014-07-01 19:15:59" 592 | }, 593 | { 594 | "name": "event-centric/when", 595 | "version": "dev-master", 596 | "source": { 597 | "type": "git", 598 | "url": "/Users/simensen/workspaces/event-centric/when", 599 | "reference": "510dfb3dd973a71b4be9f6d1a582c2deb3b2bc98" 600 | }, 601 | "require": { 602 | "event-centric/domain-events": "~0.0@dev", 603 | "mathiasverraes/classfunctions": "1.*", 604 | "php": ">=5.5" 605 | }, 606 | "type": "library", 607 | "extra": { 608 | "branch-alias": { 609 | "dev-master": "0.0.x-dev" 610 | } 611 | }, 612 | "autoload": { 613 | "psr-4": { 614 | "EventCentric\\When\\": [ 615 | "src/EventCentric/When" 616 | ] 617 | } 618 | }, 619 | "autoload-dev": { 620 | "psr-4": { 621 | "EventCentric\\When\\": [ 622 | "tests/EventCentric/When" 623 | ] 624 | } 625 | }, 626 | "license": [ 627 | "MIT" 628 | ], 629 | "authors": [ 630 | { 631 | "name": "Mathias Verraes", 632 | "email": "mathias@verraes.net", 633 | "homepage": "http://verraes.net" 634 | } 635 | ], 636 | "description": "Delegate Domain Events to event-specific methods", 637 | "homepage": "https://github.com/event-centric/when", 638 | "keywords": [ 639 | "cqrs", 640 | "ddd", 641 | "domain-driven design", 642 | "event sourcing", 643 | "reactive" 644 | ], 645 | "time": "2014-07-08 12:15:13" 646 | }, 647 | { 648 | "name": "mathiasverraes/classfunctions", 649 | "version": "1.0.1", 650 | "source": { 651 | "type": "git", 652 | "url": "https://github.com/mathiasverraes/classfunctions.git", 653 | "reference": "48e200ad665049acebf29310066e054baafdf3ce" 654 | }, 655 | "dist": { 656 | "type": "zip", 657 | "url": "https://api.github.com/repos/mathiasverraes/classfunctions/zipball/48e200ad665049acebf29310066e054baafdf3ce", 658 | "reference": "48e200ad665049acebf29310066e054baafdf3ce", 659 | "shasum": "" 660 | }, 661 | "require": { 662 | "php": ">=5.4" 663 | }, 664 | "require-dev": { 665 | "phpunit/phpunit": "*" 666 | }, 667 | "type": "library", 668 | "extra": { 669 | "branch-alias": { 670 | "dev-master": "1.0-dev" 671 | } 672 | }, 673 | "autoload": { 674 | "psr-0": { 675 | "Verraes": "src" 676 | } 677 | }, 678 | "notification-url": "https://packagist.org/downloads/", 679 | "license": [ 680 | "MIT" 681 | ], 682 | "authors": [ 683 | { 684 | "name": "Mathias Verraes", 685 | "email": "mathias@verraes.net", 686 | "homepage": "http://verraes.net", 687 | "role": "Developer" 688 | } 689 | ], 690 | "description": "Functions to manipulate class names", 691 | "homepage": "http://verraes.net", 692 | "keywords": [ 693 | "classes", 694 | "utilities" 695 | ], 696 | "time": "2014-02-06 22:47:35" 697 | } 698 | ], 699 | "packages-dev": [ 700 | { 701 | "name": "phpunit/php-code-coverage", 702 | "version": "1.2.17", 703 | "source": { 704 | "type": "git", 705 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 706 | "reference": "6ef2bf3a1c47eca07ea95f0d8a902a6340390b34" 707 | }, 708 | "dist": { 709 | "type": "zip", 710 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6ef2bf3a1c47eca07ea95f0d8a902a6340390b34", 711 | "reference": "6ef2bf3a1c47eca07ea95f0d8a902a6340390b34", 712 | "shasum": "" 713 | }, 714 | "require": { 715 | "php": ">=5.3.3", 716 | "phpunit/php-file-iterator": ">=1.3.0@stable", 717 | "phpunit/php-text-template": ">=1.2.0@stable", 718 | "phpunit/php-token-stream": ">=1.1.3@stable" 719 | }, 720 | "require-dev": { 721 | "phpunit/phpunit": "3.7.*@dev" 722 | }, 723 | "suggest": { 724 | "ext-dom": "*", 725 | "ext-xdebug": ">=2.0.5" 726 | }, 727 | "type": "library", 728 | "extra": { 729 | "branch-alias": { 730 | "dev-master": "1.2.x-dev" 731 | } 732 | }, 733 | "autoload": { 734 | "classmap": [ 735 | "PHP/" 736 | ] 737 | }, 738 | "notification-url": "https://packagist.org/downloads/", 739 | "include-path": [ 740 | "" 741 | ], 742 | "license": [ 743 | "BSD-3-Clause" 744 | ], 745 | "authors": [ 746 | { 747 | "name": "Sebastian Bergmann", 748 | "email": "sb@sebastian-bergmann.de", 749 | "role": "lead" 750 | } 751 | ], 752 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 753 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 754 | "keywords": [ 755 | "coverage", 756 | "testing", 757 | "xunit" 758 | ], 759 | "time": "2014-03-28 10:53:45" 760 | }, 761 | { 762 | "name": "phpunit/php-file-iterator", 763 | "version": "1.3.4", 764 | "source": { 765 | "type": "git", 766 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 767 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" 768 | }, 769 | "dist": { 770 | "type": "zip", 771 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", 772 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", 773 | "shasum": "" 774 | }, 775 | "require": { 776 | "php": ">=5.3.3" 777 | }, 778 | "type": "library", 779 | "autoload": { 780 | "classmap": [ 781 | "File/" 782 | ] 783 | }, 784 | "notification-url": "https://packagist.org/downloads/", 785 | "include-path": [ 786 | "" 787 | ], 788 | "license": [ 789 | "BSD-3-Clause" 790 | ], 791 | "authors": [ 792 | { 793 | "name": "Sebastian Bergmann", 794 | "email": "sb@sebastian-bergmann.de", 795 | "role": "lead" 796 | } 797 | ], 798 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 799 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 800 | "keywords": [ 801 | "filesystem", 802 | "iterator" 803 | ], 804 | "time": "2013-10-10 15:34:57" 805 | }, 806 | { 807 | "name": "phpunit/php-text-template", 808 | "version": "1.2.0", 809 | "source": { 810 | "type": "git", 811 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 812 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" 813 | }, 814 | "dist": { 815 | "type": "zip", 816 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", 817 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", 818 | "shasum": "" 819 | }, 820 | "require": { 821 | "php": ">=5.3.3" 822 | }, 823 | "type": "library", 824 | "autoload": { 825 | "classmap": [ 826 | "Text/" 827 | ] 828 | }, 829 | "notification-url": "https://packagist.org/downloads/", 830 | "include-path": [ 831 | "" 832 | ], 833 | "license": [ 834 | "BSD-3-Clause" 835 | ], 836 | "authors": [ 837 | { 838 | "name": "Sebastian Bergmann", 839 | "email": "sb@sebastian-bergmann.de", 840 | "role": "lead" 841 | } 842 | ], 843 | "description": "Simple template engine.", 844 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 845 | "keywords": [ 846 | "template" 847 | ], 848 | "time": "2014-01-30 17:20:04" 849 | }, 850 | { 851 | "name": "phpunit/php-timer", 852 | "version": "1.0.5", 853 | "source": { 854 | "type": "git", 855 | "url": "https://github.com/sebastianbergmann/php-timer.git", 856 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" 857 | }, 858 | "dist": { 859 | "type": "zip", 860 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 861 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 862 | "shasum": "" 863 | }, 864 | "require": { 865 | "php": ">=5.3.3" 866 | }, 867 | "type": "library", 868 | "autoload": { 869 | "classmap": [ 870 | "PHP/" 871 | ] 872 | }, 873 | "notification-url": "https://packagist.org/downloads/", 874 | "include-path": [ 875 | "" 876 | ], 877 | "license": [ 878 | "BSD-3-Clause" 879 | ], 880 | "authors": [ 881 | { 882 | "name": "Sebastian Bergmann", 883 | "email": "sb@sebastian-bergmann.de", 884 | "role": "lead" 885 | } 886 | ], 887 | "description": "Utility class for timing", 888 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 889 | "keywords": [ 890 | "timer" 891 | ], 892 | "time": "2013-08-02 07:42:54" 893 | }, 894 | { 895 | "name": "phpunit/php-token-stream", 896 | "version": "1.2.2", 897 | "source": { 898 | "type": "git", 899 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 900 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" 901 | }, 902 | "dist": { 903 | "type": "zip", 904 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", 905 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", 906 | "shasum": "" 907 | }, 908 | "require": { 909 | "ext-tokenizer": "*", 910 | "php": ">=5.3.3" 911 | }, 912 | "type": "library", 913 | "extra": { 914 | "branch-alias": { 915 | "dev-master": "1.2-dev" 916 | } 917 | }, 918 | "autoload": { 919 | "classmap": [ 920 | "PHP/" 921 | ] 922 | }, 923 | "notification-url": "https://packagist.org/downloads/", 924 | "include-path": [ 925 | "" 926 | ], 927 | "license": [ 928 | "BSD-3-Clause" 929 | ], 930 | "authors": [ 931 | { 932 | "name": "Sebastian Bergmann", 933 | "email": "sb@sebastian-bergmann.de", 934 | "role": "lead" 935 | } 936 | ], 937 | "description": "Wrapper around PHP's tokenizer extension.", 938 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 939 | "keywords": [ 940 | "tokenizer" 941 | ], 942 | "time": "2014-03-03 05:10:30" 943 | }, 944 | { 945 | "name": "phpunit/phpunit", 946 | "version": "3.7.37", 947 | "source": { 948 | "type": "git", 949 | "url": "https://github.com/sebastianbergmann/phpunit.git", 950 | "reference": "ae6cefd7cc84586a5ef27e04bae11ee940ec63dc" 951 | }, 952 | "dist": { 953 | "type": "zip", 954 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ae6cefd7cc84586a5ef27e04bae11ee940ec63dc", 955 | "reference": "ae6cefd7cc84586a5ef27e04bae11ee940ec63dc", 956 | "shasum": "" 957 | }, 958 | "require": { 959 | "ext-ctype": "*", 960 | "ext-dom": "*", 961 | "ext-json": "*", 962 | "ext-pcre": "*", 963 | "ext-reflection": "*", 964 | "ext-spl": "*", 965 | "php": ">=5.3.3", 966 | "phpunit/php-code-coverage": "~1.2", 967 | "phpunit/php-file-iterator": "~1.3", 968 | "phpunit/php-text-template": "~1.1", 969 | "phpunit/php-timer": "~1.0", 970 | "phpunit/phpunit-mock-objects": "~1.2", 971 | "symfony/yaml": "~2.0" 972 | }, 973 | "require-dev": { 974 | "pear-pear.php.net/pear": "1.9.4" 975 | }, 976 | "suggest": { 977 | "phpunit/php-invoker": "~1.1" 978 | }, 979 | "bin": [ 980 | "composer/bin/phpunit" 981 | ], 982 | "type": "library", 983 | "extra": { 984 | "branch-alias": { 985 | "dev-master": "3.7.x-dev" 986 | } 987 | }, 988 | "autoload": { 989 | "classmap": [ 990 | "PHPUnit/" 991 | ] 992 | }, 993 | "notification-url": "https://packagist.org/downloads/", 994 | "include-path": [ 995 | "", 996 | "../../symfony/yaml/" 997 | ], 998 | "license": [ 999 | "BSD-3-Clause" 1000 | ], 1001 | "authors": [ 1002 | { 1003 | "name": "Sebastian Bergmann", 1004 | "email": "sebastian@phpunit.de", 1005 | "role": "lead" 1006 | } 1007 | ], 1008 | "description": "The PHP Unit Testing framework.", 1009 | "homepage": "http://www.phpunit.de/", 1010 | "keywords": [ 1011 | "phpunit", 1012 | "testing", 1013 | "xunit" 1014 | ], 1015 | "time": "2014-04-30 12:24:19" 1016 | }, 1017 | { 1018 | "name": "phpunit/phpunit-mock-objects", 1019 | "version": "1.2.3", 1020 | "source": { 1021 | "type": "git", 1022 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 1023 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" 1024 | }, 1025 | "dist": { 1026 | "type": "zip", 1027 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", 1028 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", 1029 | "shasum": "" 1030 | }, 1031 | "require": { 1032 | "php": ">=5.3.3", 1033 | "phpunit/php-text-template": ">=1.1.1@stable" 1034 | }, 1035 | "suggest": { 1036 | "ext-soap": "*" 1037 | }, 1038 | "type": "library", 1039 | "autoload": { 1040 | "classmap": [ 1041 | "PHPUnit/" 1042 | ] 1043 | }, 1044 | "notification-url": "https://packagist.org/downloads/", 1045 | "include-path": [ 1046 | "" 1047 | ], 1048 | "license": [ 1049 | "BSD-3-Clause" 1050 | ], 1051 | "authors": [ 1052 | { 1053 | "name": "Sebastian Bergmann", 1054 | "email": "sb@sebastian-bergmann.de", 1055 | "role": "lead" 1056 | } 1057 | ], 1058 | "description": "Mock Object library for PHPUnit", 1059 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 1060 | "keywords": [ 1061 | "mock", 1062 | "xunit" 1063 | ], 1064 | "time": "2013-01-13 10:24:48" 1065 | }, 1066 | { 1067 | "name": "symfony/yaml", 1068 | "version": "v2.5.1", 1069 | "target-dir": "Symfony/Component/Yaml", 1070 | "source": { 1071 | "type": "git", 1072 | "url": "https://github.com/symfony/Yaml.git", 1073 | "reference": "1057e87364c0b38b50f5695fc9df9dd189036bec" 1074 | }, 1075 | "dist": { 1076 | "type": "zip", 1077 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/1057e87364c0b38b50f5695fc9df9dd189036bec", 1078 | "reference": "1057e87364c0b38b50f5695fc9df9dd189036bec", 1079 | "shasum": "" 1080 | }, 1081 | "require": { 1082 | "php": ">=5.3.3" 1083 | }, 1084 | "type": "library", 1085 | "extra": { 1086 | "branch-alias": { 1087 | "dev-master": "2.5-dev" 1088 | } 1089 | }, 1090 | "autoload": { 1091 | "psr-0": { 1092 | "Symfony\\Component\\Yaml\\": "" 1093 | } 1094 | }, 1095 | "notification-url": "https://packagist.org/downloads/", 1096 | "license": [ 1097 | "MIT" 1098 | ], 1099 | "authors": [ 1100 | { 1101 | "name": "Fabien Potencier", 1102 | "email": "fabien@symfony.com", 1103 | "homepage": "http://fabien.potencier.org", 1104 | "role": "Lead Developer" 1105 | }, 1106 | { 1107 | "name": "Symfony Community", 1108 | "homepage": "http://symfony.com/contributors" 1109 | } 1110 | ], 1111 | "description": "Symfony Yaml Component", 1112 | "homepage": "http://symfony.com", 1113 | "time": "2014-07-08 12:21:33" 1114 | } 1115 | ], 1116 | "aliases": [ 1117 | 1118 | ], 1119 | "minimum-stability": "stable", 1120 | "stability-flags": { 1121 | "dflydev/event-store": 20, 1122 | "event-centric/domain-events": 20, 1123 | "event-centric/when": 20 1124 | }, 1125 | "platform": { 1126 | "php": ">=5.5" 1127 | }, 1128 | "platform-dev": [ 1129 | 1130 | ] 1131 | } 1132 | --------------------------------------------------------------------------------