-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extract aggregate root into traits to make it easier to avoid domain extending infrastructure #62
Changes from 3 commits
c502f65
879bfa9
6277aa0
c0f4218
cb50ee6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
/** | ||
* This file is part of the prooph/event-sourcing. | ||
* (c) 2014-2017 prooph software GmbH <contact@prooph.de> | ||
* (c) 2015-2017 Sascha-Oliver Prolic <saschaprolic@googlemail.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Prooph\EventSourcing\Aggregate; | ||
|
||
use Prooph\EventSourcing\AggregateChanged; | ||
|
||
trait EventProducerTrait | ||
{ | ||
/** | ||
* Current version | ||
* | ||
* @var int | ||
*/ | ||
protected $version = 0; | ||
|
||
/** | ||
* List of events that are not committed to the EventStore | ||
* | ||
* @var AggregateChanged[] | ||
*/ | ||
protected $recordedEvents = []; | ||
|
||
/** | ||
* Get pending events and reset stack | ||
* | ||
* @return AggregateChanged[] | ||
*/ | ||
protected function popRecordedEvents(): array | ||
{ | ||
$pendingEvents = $this->recordedEvents; | ||
|
||
$this->recordedEvents = []; | ||
|
||
return $pendingEvents; | ||
} | ||
|
||
/** | ||
* Record an aggregate changed event | ||
*/ | ||
protected function recordThat(AggregateChanged $event): void | ||
{ | ||
$this->version += 1; | ||
|
||
$this->recordedEvents[] = $event->withVersion($this->version); | ||
|
||
$this->apply($event); | ||
} | ||
|
||
/** | ||
* Apply given event | ||
*/ | ||
abstract protected function apply(AggregateChanged $event): void; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
/** | ||
* This file is part of the prooph/event-sourcing. | ||
* (c) 2014-2017 prooph software GmbH <contact@prooph.de> | ||
* (c) 2015-2017 Sascha-Oliver Prolic <saschaprolic@googlemail.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Prooph\EventSourcing\Aggregate; | ||
|
||
use Iterator; | ||
use Prooph\EventSourcing\AggregateChanged; | ||
use RuntimeException; | ||
|
||
trait EventSourcedTrait | ||
{ | ||
/** | ||
* Current version | ||
* | ||
* @var int | ||
*/ | ||
protected $version = 0; | ||
|
||
/** | ||
* @throws RuntimeException | ||
*/ | ||
protected static function reconstituteFromHistory(Iterator $historyEvents): self | ||
{ | ||
$instance = new static(); | ||
$instance->replay($historyEvents); | ||
|
||
return $instance; | ||
} | ||
|
||
/** | ||
* Replay past events | ||
* | ||
* @throws RuntimeException | ||
*/ | ||
protected function replay(Iterator $historyEvents): void | ||
{ | ||
foreach ($historyEvents as $pastEvent) { | ||
/** @var AggregateChanged $pastEvent */ | ||
$this->version = $pastEvent->version(); | ||
|
||
$this->apply($pastEvent); | ||
} | ||
} | ||
|
||
/** | ||
* Apply given event | ||
*/ | ||
abstract protected function apply(AggregateChanged $event): void; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<?php | ||
/** | ||
* This file is part of the prooph/event-sourcing. | ||
* (c) 2014-2017 prooph software GmbH <contact@prooph.de> | ||
* (c) 2015-2017 Sascha-Oliver Prolic <saschaprolic@googlemail.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Prooph\EventSourcing\EventStoreIntegration; | ||
|
||
use Iterator; | ||
use Prooph\Common\Messaging\Message; | ||
use Prooph\EventSourcing\Aggregate\AggregateTranslator as EventStoreAggregateTranslator; | ||
use Prooph\EventSourcing\Aggregate\AggregateType; | ||
use RuntimeException; | ||
|
||
final class ClosureAggregateTranslator implements EventStoreAggregateTranslator | ||
{ | ||
protected $aggregateIdExtractor; | ||
protected $aggregateReconstructor; | ||
protected $pendingEventsExtractor; | ||
protected $replayStreamEvents; | ||
protected $versionExtractor; | ||
|
||
/** | ||
* @param object $eventSourcedAggregateRoot | ||
* | ||
* @return int | ||
*/ | ||
public function extractAggregateVersion($eventSourcedAggregateRoot): int | ||
{ | ||
if (null === $this->versionExtractor) { | ||
$this->versionExtractor = function (): int { | ||
return $this->version; | ||
}; | ||
} | ||
|
||
return $this->versionExtractor->call($eventSourcedAggregateRoot); | ||
} | ||
|
||
/** | ||
* @param object $anEventSourcedAggregateRoot | ||
* | ||
* @return string | ||
*/ | ||
public function extractAggregateId($anEventSourcedAggregateRoot): string | ||
{ | ||
if (null === $this->aggregateIdExtractor) { | ||
$this->aggregateIdExtractor = function (): string { | ||
return $this->aggregateId(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is the problem from above. It is no longer guaranteed and the user gets no hint that such an |
||
}; | ||
} | ||
|
||
return $this->aggregateIdExtractor->call($anEventSourcedAggregateRoot); | ||
} | ||
|
||
/** | ||
* @param AggregateType $aggregateType | ||
* @param Iterator $historyEvents | ||
* | ||
* @return object reconstructed AggregateRoot | ||
*/ | ||
public function reconstituteAggregateFromHistory(AggregateType $aggregateType, Iterator $historyEvents) | ||
{ | ||
if (null === $this->aggregateReconstructor) { | ||
$this->aggregateReconstructor = function ($historyEvents) { | ||
return static::reconstituteFromHistory($historyEvents); | ||
}; | ||
} | ||
|
||
$arClass = $aggregateType->toString(); | ||
|
||
if (! class_exists($arClass)) { | ||
throw new RuntimeException( | ||
sprintf('Aggregate root class %s cannot be found', $arClass) | ||
); | ||
} | ||
|
||
return ($this->aggregateReconstructor->bindTo(null, $arClass))($historyEvents); | ||
} | ||
|
||
/** | ||
* @param object $anEventSourcedAggregateRoot | ||
* | ||
* @return Message[] | ||
*/ | ||
public function extractPendingStreamEvents($anEventSourcedAggregateRoot): array | ||
{ | ||
if (null === $this->pendingEventsExtractor) { | ||
$this->pendingEventsExtractor = function (): array { | ||
return $this->popRecordedEvents(); | ||
}; | ||
} | ||
|
||
return $this->pendingEventsExtractor->call($anEventSourcedAggregateRoot); | ||
} | ||
|
||
/** | ||
* @param object $anEventSourcedAggregateRoot | ||
* @param Iterator $events | ||
* | ||
* @return void | ||
*/ | ||
public function replayStreamEvents($anEventSourcedAggregateRoot, Iterator $events): void | ||
{ | ||
if (null === $this->replayStreamEvents) { | ||
$this->replayStreamEvents = function ($events): void { | ||
$this->replay($events); | ||
}; | ||
} | ||
$this->replayStreamEvents->call($anEventSourcedAggregateRoot, $events); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is that not part of the event sourced trait?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess because a trait cannot contain abstract methods to force the using class to implement it
@Xerkus Any ideas how to improve this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh was late yesterday. Need to revert my comment. A trait can contain abstract methods ...