protected function createEventStream($identifier, array $event_stream_data) { $events = new AggregateRootEventList(); foreach ($event_stream_data as $event_data) { $event_data = $event_data['doc']; if (!isset($event_data[self::OBJECT_TYPE])) { throw new RuntimeError("Missing type key within event data."); } $event_class = $event_data[self::OBJECT_TYPE]; $events->addItem(new $event_class($event_data)); } $data['identifier'] = $identifier; $data['events'] = $events; return new EventStream($data); }
/** * Takes an event and applies the resulting state change to the aggregate-root's internal state. * * @param AggregateRootEventInterface $event * @param bool $auto_commit Whether to directly add the given event to the uncomitted-events list. * * @return AggregateRootEventInterface Event that is acutally applied and comitted or false if the AR is invalid. */ protected function applyEvent(AggregateRootEventInterface $event, $auto_commit = true) { $this->guardEventPreConditions($event); if (!$this->setValues($event->getData())) { foreach ($this->getValidationResults() as $validation_result) { foreach ($validation_result->getViolatedRules() as $violated_rule) { foreach ($violated_rule->getIncidents() as $incident) { $errors[] = PHP_EOL . $validation_result->getSUbject()->getName() . ' - ' . $violated_rule->getName() . ' > ' . $incident->getName() . ': ' . print_r($incident->getParameters(), true); } } } throw new RuntimeError(sprintf("Aggregate-root is in an invalid state after applying %s.\nErrors:%s", get_class($event), implode(PHP_EOL, $errors))); } $embedded_entity_events = new EmbeddedEntityEventList(); foreach ($event->getEmbeddedEntityEvents() as $embedded_entity_event) { $embedded_entity_events->push($this->applyEmbeddedEntityEvent($embedded_entity_event)); } $source_event = null; if ($auto_commit) { $recorded_changes = $this->getRecordedChanges(); if (!empty($recorded_changes) || !$embedded_entity_events->isEmpty()) { $source_event = $event->createCopyWith(['data' => $recorded_changes, 'embedded_entity_events' => $embedded_entity_events]); $this->uncomitted_events_list->push($source_event); $this->history->push($source_event); } } else { $source_event = $event; } if ($source_event) { $this->setValue('revision', $source_event->getSeqNumber()); $this->markClean(); } else { $notice = 'Applied event %s for %s did not trigger any state changes, so it is being dropped ...'; error_log(sprintf($notice, $event, $this)); } return $source_event; }
protected function createAggregateRootFromHistory(AggregateRootEventList $history, $target_revision = null) { $target_revision = $target_revision ?: $history->getLast()->getSeqNumber(); if ($history->getLast()->getSeqNumber() > $target_revision) { $known_history = $history->filter(function (AggregateRootEventInterface $event) use($target_revision) { return $event->getSeqNumber() <= $target_revision; }); } else { $known_history = $history; } $aggregate_root = $this->getAggregateRootType()->createEntity(); $aggregate_root->reconstituteFrom($known_history); return $aggregate_root; }
protected function getHistoryFixture() { $history_fixture = new AggregateRootEventList(); $history_fixture->push(new AuthorCreatedEvent(['aggregate_root_type' => self::AGGREGATE_ROOT_TYPE, 'aggregate_root_identifier' => self::AGGREGATE_ROOT_IDENTIFIER, 'uuid' => '26cfb993-5946-4bd3-befe-8fb92648fd27', 'seq_number' => 1, 'data' => ['firstname' => 'Mark', 'lastname' => 'Twain', 'blurb' => 'the grinch', 'identifier' => self::AGGREGATE_ROOT_IDENTIFIER, 'uuid' => self::AGGREGATE_ROOT_UUID, 'language' => self::AGGREGATE_ROOT_LANGUAGE, 'version' => 1, 'workflow_state' => 'inactive', 'workflow_parameters' => []]])); $history_fixture->push(new AuthorModifiedEvent(['aggregate_root_type' => self::AGGREGATE_ROOT_TYPE, 'aggregate_root_identifier' => self::AGGREGATE_ROOT_IDENTIFIER, 'uuid' => '9d3cefd9-f5f1-4a3f-ad2b-8d146d50eba7', 'seq_number' => 2, 'data' => ['lastname' => 'Wahlberg']])); $history_fixture->push(new AuthorModifiedEvent(['aggregate_root_type' => self::AGGREGATE_ROOT_TYPE, 'aggregate_root_identifier' => self::AGGREGATE_ROOT_IDENTIFIER, 'uuid' => '39e3d80a-d700-4c1f-8bc7-0c3141b94af7', 'seq_number' => 3, 'data' => ['firstname' => 'Donnie']])); $history_fixture->push(new AuthorModifiedEvent(['aggregate_root_type' => self::AGGREGATE_ROOT_TYPE, 'aggregate_root_identifier' => self::AGGREGATE_ROOT_IDENTIFIER, 'uuid' => '6d3f60a0-3662-47ad-a7f0-1eaf33bb46b0', 'seq_number' => 4, 'data' => ['lastname' => 'Darko']])); return $history_fixture; }
protected function loadHistory() { $identifier = $this->getData($this->getArgument()); if ($identifier instanceof EntityInterface) { $identifier = $identifier->getIdentifier(); } $query_result = $this->getDomainEventQueryService()->findEventsByIdentifier($identifier); $history = new AggregateRootEventList($query_result->getResults()); if ($history->isEmpty()) { return null; } return $history; }
/** * Commit all changes that are pending for our tracked aggregate-roots. * * @return AggregateRootEventList Returns a list of events that were actually committed. */ public function commit() { $committed_events_map = new AggregateRootEventListMap(); $comitted_ars = []; foreach ($this->tracked_aggregate_roots as $aggregate_root) { $event_stream = $this->tracked_aggregate_roots[$aggregate_root]; $committed_events_list = new AggregateRootEventList(); foreach ($aggregate_root->getUncomittedEvents() as $uncomitted_event) { $event_stream->push($uncomitted_event); $this->event_writer->write($uncomitted_event); $committed_events_list->push($uncomitted_event); } $aggregate_root->markAsComitted(); $committed_events_map->setItem($aggregate_root->getIdentifier(), $committed_events_list); $comitted_ars[] = $aggregate_root; } foreach ($comitted_ars as $comitted_ar) { $this->tracked_aggregate_roots->offsetUnset($aggregate_root); } return $committed_events_map; }