Skip to content

Commit

Permalink
unify events load
Browse files Browse the repository at this point in the history
  • Loading branch information
juliangut committed Sep 25, 2019
1 parent 4bac893 commit da39930
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 99 deletions.
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ class CustomAggregate extends AbstractAggregateRoot
}
```

#### Aggregate Event
#### Aggregate operations

Every operation in aggregates should be made through aggregate events, even aggregate's own creation (see example above), that's the reason AbstractAggregateRoot constructor is protected.

Aggregate events represent facts relevant to the Event Sourcing system such AggregateCreated and SomethingFinished in the previous example

Aggregate events should be finally collected and persisted on an event store
Aggregate events should be finally collected, persisted on an event store and eventually dispatched to an event bus depending on the Projection mechanism at work

```php
$aggregateId = CustomAggregateIdentity::fromString('4c4316cb-b48b-44fb-a034-90d789966bac');
Expand All @@ -140,7 +140,7 @@ foreach ($customAggregate->collectRecordedAggregateEvents() as $aggregateEvent)

Aggregate roots can collect two fundamentally different types of events, Aggregate events have already been discussed in the section above, the second kind of events are Domain events which represent facts relevant to the Domain

They differ conceptually on who are this events relevant or meant to. While Aggregate Events are meant _only_ for the Event Sourcing system, that is Event Store persistence, Aggregate root reconstruction and optionally other derivatives, Domain Events are relevant to the application Domain in itself, that is this or other parts or Bounded Contexts, of your system
They differ drastically and conceptually on who are this events relevant or meant to. While Aggregate Events are meant _only_ for the Event Sourcing system, that is Event Store persistence, Aggregate root reconstruction, Projection, Snapshotting, Sagas and optionally other derivatives, Domain Events are relevant to the application Domain in itself, that is this or other parts or Bounded Contexts, of your system

Domain events must be collected and sent to an event bus, head to [gears/event](https://github.com/phpgears/event) to learn more about this topic

Expand All @@ -153,22 +153,28 @@ foreach ($customAggregate->collectRecordedEvents() as $domainEvent) {

##### Event granularity

Granularity is key when dealing with this two kinds of events. As a rule of thumb you can consider that Aggregate Events can be mapped to atomic actions that can be performed on an aggregate root, while Domain events can be seen as actions that a user can perform on the domain
Granularity is key when dealing with this two kinds of events. As a rule of thumb you can consider that Aggregate Events can be mapped to atomic actions that can be performed on an aggregate root, while Domain events can be seen as actions that a user can perform on the domain, they will naturally have a 1:1 relationship in most cases, but not necessarily in all cases

An example that can be of some help to understand this difference is a two step process of user registration in a system with email validation
An example that can be of some help to understand this difference is a two step process of user sign up in a system with email validation

While on a first step a user is created in the system (data fully or partially filled) the user cannot be considered completely registered until he validates his email in a second step
While on a first step a user is created in the system (data is fully or partially collected) the user cannot be considered completely registered until he validates his email on a second step

You can consider two methods on the User aggregate to accomplish this task, createUser() that creates the user Entity with whatever data provided, and validateUser() which validates the used based for example on a code sent to his email
You can consider two methods on the User aggregate to accomplish this task, createUser() that creates the user Aggregate Root with whatever data provided, and validateUser() which validates the user based for example on a code sent to his email

Both of these methods will create an Aggregate Event that will be persisted in the Event Store but only the later will create a UserRegistered Domain event which can be relevant for other parts of your system (i.e. creating a user wallet)
Both of these methods will create an Aggregate Event that will be eventually persisted onto the Event Store, but only the later will create a UserRegistered domain event which can be relevant for other parts of your system (e.g. creating a user wallet on the billing Bounded Context)

### Aggregate Repository



### Event Store



### Snapshot Store



## Contributing

Found a bug or have a feature request? [Please open a new issue](https://github.com/phpgears/event-sourcing/issues). Have a look at existing issues before.
Expand Down
2 changes: 1 addition & 1 deletion src/Event/AggregateEventEmptyStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ final class AggregateEventEmptyStream extends \EmptyIterator implements Aggregat
*/
public function current(): AggregateEvent
{
throw new AggregateEventStreamException('Current method must not be called on AggregateEventEmptyStream');
throw new AggregateEventStreamException('"Current" method must not be called on AggregateEventEmptyStream');
}

/**
Expand Down
53 changes: 21 additions & 32 deletions src/Store/Event/AbstractEventStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ public function loadFrom(
return new AggregateEventEmptyStream();
}

return $this->loadEventsFrom($stream, $fromVersion, $count);
$toVersion = $count !== null
? new AggregateVersion($fromVersion->getValue() + $count - 1)
: null;

return $this->loadEvents($stream, $fromVersion, $toVersion);
}

/**
Expand Down Expand Up @@ -85,9 +89,24 @@ public function loadTo(
return new AggregateEventEmptyStream();
}

return $this->loadEventsTo($stream, $toVersion, $fromVersion);
return $this->loadEvents($stream, $fromVersion, $toVersion);
}

/**
* Get aggregate event stream.
*
* @param StoreStream $stream
* @param AggregateVersion $fromVersion
* @param AggregateVersion|null $toVersion
*
* @return AggregateEventStream
*/
abstract protected function loadEvents(
StoreStream $stream,
AggregateVersion $fromVersion,
?AggregateVersion $toVersion = null
): AggregateEventStream;

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -127,36 +146,6 @@ public function store(StoreStream $stream, AggregateEventStream $eventStream): v
$this->storeEvents($stream, $eventStream);
}

/**
* Load aggregate events from a version.
*
* @param StoreStream $stream
* @param AggregateVersion $fromVersion
* @param int|null $count
*
* @return AggregateEventStream
*/
abstract protected function loadEventsFrom(
StoreStream $stream,
AggregateVersion $fromVersion,
?int $count = null
): AggregateEventStream;

/**
* Load aggregate events up to a version.
*
* @param StoreStream $stream
* @param AggregateVersion $toVersion
* @param AggregateVersion $fromVersion
*
* @return AggregateEventStream
*/
abstract protected function loadEventsTo(
StoreStream $stream,
AggregateVersion $toVersion,
AggregateVersion $fromVersion
): AggregateEventStream;

/**
* Append events to store.
*
Expand Down
42 changes: 6 additions & 36 deletions src/Store/Event/InMemoryEventStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use Gears\EventSourcing\Aggregate\AggregateVersion;
use Gears\EventSourcing\Event\AggregateEvent;
use Gears\EventSourcing\Event\AggregateEventEmptyStream;
use Gears\EventSourcing\Event\AggregateEventIteratorStream;
use Gears\EventSourcing\Event\AggregateEventStream;
use Gears\EventSourcing\Store\StoreStream;
Expand All @@ -34,56 +35,25 @@ final class InMemoryEventStore extends AbstractEventStore
/**
* {@inheritdoc}
*/
protected function loadEventsFrom(
protected function loadEvents(
StoreStream $stream,
AggregateVersion $fromVersion,
?int $count = null
?AggregateVersion $toVersion = null
): AggregateEventStream {
$events = new \ArrayObject();

$streamId = $this->getStreamId($stream);
if (!isset($this->streams[$streamId][$fromVersion->getValue()])) {
// @codeCoverageIgnoreStart
return new AggregateEventIteratorStream($events->getIterator());
return new AggregateEventEmptyStream();
// @codeCoverageIgnoreEnd
}

foreach ($this->streams[$streamId] as $version => $event) {
if ($version >= $fromVersion->getValue()) {
$events->append($event);
}

if ($count !== null && \count($events) === $count) {
break;
}
}

return new AggregateEventIteratorStream($events->getIterator());
}

/**
* {@inheritdoc}
*/
protected function loadEventsTo(
StoreStream $stream,
AggregateVersion $toVersion,
AggregateVersion $fromVersion
): AggregateEventStream {
$events = new \ArrayObject();

$streamId = $this->getStreamId($stream);
if (!isset($this->streams[$streamId][$fromVersion->getValue()])) {
// @codeCoverageIgnoreStart
return new AggregateEventIteratorStream($events->getIterator());
// @codeCoverageIgnoreEnd
}

foreach ($this->streams[$streamId] as $version => $event) {
if ($version >= $fromVersion->getValue() && $version <= $toVersion->getValue()) {
if ($version >= $fromVersion->getValue() && ($toVersion === null || $version <= $toVersion->getValue())) {
$events->append($event);
}

if ($version >= $toVersion->getValue()) {
if ($toVersion !== null && $version >= $toVersion->getValue()) {
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AggregateEventEmptyStreamTest extends TestCase
public function testInvalidTypeStream(): void
{
$this->expectException(AggregateEventStreamException::class);
$this->expectExceptionMessage('Current method must not be called on AggregateEventEmptyStream');
$this->expectExceptionMessage('"Current" method must not be called on AggregateEventEmptyStream');

(new AggregateEventEmptyStream())->current();
}
Expand Down
33 changes: 12 additions & 21 deletions tests/EventSourcing/Stub/AbstractEventStoreStub.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,31 +77,22 @@ protected function createStream(StoreStream $stream): void
/**
* {@inheritdoc}
*/
protected function loadEventsFrom(
protected function loadEvents(
StoreStream $stream,
AggregateVersion $fromVersion,
?int $count = null
?AggregateVersion $toVersion = null
): AggregateEventStream {
return new AggregateEventIteratorStream((new \ArrayObject(\array_slice(
$this->stream,
$fromVersion->getValue() - 1,
$count
)))->getIterator());
}
if ($toVersion !== null) {
$events = \array_slice(
$this->stream,
$fromVersion->getValue() - 1,
$toVersion->getValue() - $fromVersion->getValue() + 1
);
} else {
$events = \array_slice($this->stream, $fromVersion->getValue() - 1);
}

/**
* {@inheritdoc}
*/
protected function loadEventsTo(
StoreStream $stream,
AggregateVersion $toVersion,
AggregateVersion $fromVersion
): AggregateEventStream {
return new AggregateEventIteratorStream((new \ArrayObject(\array_slice(
$this->stream,
$fromVersion->getValue() - 1,
$toVersion->getValue() - $fromVersion->getValue() + 1
)))->getIterator());
return new AggregateEventIteratorStream((new \ArrayObject($events))->getIterator());
}

/**
Expand Down

0 comments on commit da39930

Please sign in to comment.