Skip to content

Commit

Permalink
tweak serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
juliangut committed Mar 7, 2020
1 parent 42bfdd9 commit 25163ac
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 60 deletions.
2 changes: 2 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ parameters:
paths:
- src/Event/AggregateEventArrayStream.php
- src/Event/AggregateEventIteratorStream.php
- message: '/^Variable property access on \$this\(Gears\\EventSourcing\\Aggregate\\AbstractAggregateRoot\)\.$/'
path: src/Aggregate/AbstractAggregateRoot.php
99 changes: 94 additions & 5 deletions src/Aggregate/AbstractAggregateRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Gears\Aggregate\EventBehaviour;
use Gears\EventSourcing\Aggregate\Exception\AggregateException;
use Gears\EventSourcing\Aggregate\Exception\AggregateVersionException;
use Gears\EventSourcing\Aggregate\Serializer\Exception\AggregateSerializationException;
use Gears\EventSourcing\Event\AggregateEvent;
use Gears\EventSourcing\Event\AggregateEventIteratorStream;
use Gears\EventSourcing\Event\AggregateEventStream;
Expand All @@ -31,7 +32,7 @@ abstract class AbstractAggregateRoot implements AggregateRoot
use AggregateBehaviour, EventBehaviour;

/**
* @var \ArrayObject<string, AggregateEvent>
* @var \ArrayObject<string, AggregateEvent>|null
*/
private $recordedAggregateEvents;

Expand All @@ -40,7 +41,6 @@ abstract class AbstractAggregateRoot implements AggregateRoot
*/
final protected function __construct()
{
$this->recordedAggregateEvents = new \ArrayObject();
$this->version = new AggregateVersion(0);
}

Expand Down Expand Up @@ -126,6 +126,10 @@ final protected function recordAggregateEvent(AggregateEvent $event): void
]
);

if ($this->recordedAggregateEvents === null) {
$this->recordedAggregateEvents = new \ArrayObject();
}

$this->recordedAggregateEvents->append($recordedEvent);
}

Expand Down Expand Up @@ -175,26 +179,111 @@ protected function getAggregateEventApplyMethodName(AggregateEvent $event): stri
*/
final public function getRecordedAggregateEvents(): AggregateEventStream
{
return new AggregateEventIteratorStream($this->recordedAggregateEvents->getIterator());
return new AggregateEventIteratorStream(
$this->recordedAggregateEvents !== null
? $this->recordedAggregateEvents->getIterator()
: new \EmptyIterator()
);
}

/**
* {@inheritdoc}
*/
final public function clearRecordedAggregateEvents(): void
{
$this->recordedAggregateEvents = new \ArrayObject();
$this->recordedAggregateEvents = null;
}

/**
* {@inheritdoc}
*/
final public function collectRecordedAggregateEvents(): AggregateEventStream
{
$recordedEvents = new AggregateEventIteratorStream($this->recordedAggregateEvents->getIterator());
$recordedEvents = new AggregateEventIteratorStream(
$this->recordedAggregateEvents !== null
? $this->recordedAggregateEvents->getIterator()
: new \EmptyIterator()
);

$this->recordedAggregateEvents = new \ArrayObject();

return $recordedEvents;
}

/**
* @return array<string, mixed>
*/
final public function __serialize(): array
{
return $this->getSerializationAttributes();
}

/**
* @param array<string, mixed> $data
*/
final public function __unserialize(array $data): void
{
$this->unserializeAttributes($data);
}

/**
* {@inheritdoc}
*/
final public function serialize(): string
{
return \serialize($this->getSerializationAttributes());
}

/**
* {@inheritdoc}
*
* @param mixed $serialized
*/
final public function unserialize($serialized): void
{
$this->unserializeAttributes(\unserialize($serialized));
}

/**
* Get serialization data.
*
* @throws AggregateSerializationException
*
* @return array<string, mixed>
*/
private function getSerializationAttributes(): array
{
if (($this->recordedAggregateEvents !== null && $this->recordedAggregateEvents->count() !== 0)
|| ($this->recordedEvents !== null && $this->recordedEvents->count() !== 0)
) {
throw new AggregateSerializationException('Aggregate root with recorded events cannot be serialized');
}

$attributes = [];
foreach ((new \ReflectionObject($this))->getProperties() as $reflectionProperty) {
if (!$reflectionProperty->isStatic()) {
$reflectionProperty->setAccessible(true);
$attributes[$reflectionProperty->getName()] = $reflectionProperty->getValue($this);
}
}

$attributes['identity'] = $this->identity;
$attributes['version'] = $this->version;

return $attributes;
}

/**
* Unserialize attributes.
*
* @param array<string, mixed> $attributes
*/
private function unserializeAttributes(array $attributes): void
{
foreach ($attributes as $attribute => $value) {
if (!\in_array($attribute, ['recordedAggregateEvents', 'recordedEvents'], true)) {
$this->{$attribute} = $value;
}
}
}
}
2 changes: 1 addition & 1 deletion src/Aggregate/AggregateRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
/**
* AggregateRoot interface.
*/
interface AggregateRoot extends BaseAggregateRoot
interface AggregateRoot extends BaseAggregateRoot, \Serializable
{
/**
* Get aggregate version.
Expand Down
36 changes: 35 additions & 1 deletion src/Aggregate/AggregateVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

use Gears\EventSourcing\Aggregate\Exception\AggregateVersionException;

final class AggregateVersion
final class AggregateVersion implements \Serializable
{
/**
* @var int
Expand Down Expand Up @@ -91,4 +91,38 @@ public function getPrevious(): self

return $clone;
}

/**
* @return array<string, mixed>
*/
public function __serialize(): array
{
return ['value' => $this->value];
}

/**
* @param array<string, mixed> $data
*/
public function __unserialize(array $data): void
{
$this->value = $data['value'];
}

/**
* {@inheritdoc}
*/
public function serialize(): string
{
return \serialize($this->value);
}

/**
* {@inheritdoc}
*
* @param mixed $serialized
*/
public function unserialize($serialized): void
{
$this->value = \unserialize($serialized, ['allowed_classes' => false]);
}
}
33 changes: 32 additions & 1 deletion src/Event/AbstractAggregateEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ final protected function __construct(Identity $aggregateId, array $payload, \Dat
*/
public function getEventType(): string
{
return \get_called_class();
return static::class;
}

/**
Expand Down Expand Up @@ -99,6 +99,37 @@ final public static function reconstitute(array $payload, \DateTimeImmutable $cr
return $event;
}

/**
* @return string[]
*/
final public function __sleep(): array
{
throw new AggregateEventException(\sprintf('Aggregate event "%s" cannot be serialized', static::class));
}

final public function __wakeup(): void
{
throw new AggregateEventException(\sprintf('Aggregate event "%s" cannot be unserialized', static::class));
}

/**
* @return array<string, mixed>
*/
final public function __serialize(): array
{
throw new AggregateEventException(\sprintf('Aggregate event "%s" cannot be serialized', static::class));
}

/**
* @param array<string, mixed> $data
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
final public function __unserialize(array $data): void
{
throw new AggregateEventException(\sprintf('Aggregate event "%s" cannot be unserialized', static::class));
}

/**
* {@inheritdoc}
*
Expand Down
33 changes: 32 additions & 1 deletion src/Event/AbstractEmptyAggregateEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ final protected function __construct(Identity $aggregateId, \DateTimeImmutable $
*/
public function getEventType(): string
{
return \get_called_class();
return static::class;
}

/**
Expand Down Expand Up @@ -98,6 +98,37 @@ final public static function reconstitute(array $payload, \DateTimeImmutable $cr
return $event;
}

/**
* @return array<string, mixed>
*/
final public function __serialize(): array
{
throw new AggregateEventException(\sprintf('Aggregate event "%s" cannot be serialized', static::class));
}

/**
* @param array<string, mixed> $data
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
final public function __unserialize(array $data): void
{
throw new AggregateEventException(\sprintf('Aggregate event "%s" cannot be unserialized', static::class));
}

/**
* @return string[]
*/
final public function __sleep(): array
{
throw new AggregateEventException(\sprintf('Aggregate event "%s" cannot be serialized', static::class));
}

final public function __wakeup(): void
{
throw new AggregateEventException(\sprintf('Aggregate event "%s" cannot be unserialized', static::class));
}

/**
* {@inheritdoc}
*
Expand Down
39 changes: 10 additions & 29 deletions src/Store/Snapshot/AbstractSnapshotStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

use Gears\EventSourcing\Aggregate\AggregateRoot;
use Gears\EventSourcing\Aggregate\Serializer\AggregateSerializer;
use Gears\EventSourcing\Store\Snapshot\Exception\SnapshotStoreException;

/**
* Abstract snapshot implementation.
Expand All @@ -38,44 +37,26 @@ public function __construct(AggregateSerializer $serializer)
}

/**
* Deserialize aggregate root.
*
* @param string $serialized
* Serialize aggregate root.
*
* @throws SnapshotStoreException
* @param AggregateRoot $aggregateRoot
*
* @return AggregateRoot
* @return string
*/
final protected function deserializeAggregateRoot(string $serialized): AggregateRoot
final protected function serializeAggregateRoot(AggregateRoot $aggregateRoot): string
{
$aggregateRoot = $this->serializer->fromSerialized($serialized);

if ($aggregateRoot->getRecordedAggregateEvents()->count() !== 0
|| $aggregateRoot->getRecordedEvents()->count() !== 0
) {
throw new SnapshotStoreException('Aggregate root coming from snapshot cannot have recorded events');
}

return $aggregateRoot;
return $this->serializer->serialize($aggregateRoot);
}

/**
* Serialize aggregate root.
*
* @param AggregateRoot $aggregateRoot
* Deserialize aggregate root.
*
* @throws SnapshotStoreException
* @param string $serialized
*
* @return string
* @return AggregateRoot
*/
final protected function serializeAggregateRoot(AggregateRoot $aggregateRoot): string
final protected function deserializeAggregateRoot(string $serialized): AggregateRoot
{
if ($aggregateRoot->getRecordedAggregateEvents()->count() !== 0
|| $aggregateRoot->getRecordedEvents()->count() !== 0
) {
throw new SnapshotStoreException('Aggregate root cannot have recorded events in order to be snapshoted');
}

return $this->serializer->serialize($aggregateRoot);
return $this->serializer->fromSerialized($serialized);
}
}
Loading

0 comments on commit 25163ac

Please sign in to comment.