Skip to content

Commit

Permalink
Merge 2072073 into 16eb09d
Browse files Browse the repository at this point in the history
  • Loading branch information
frankdejonge committed Sep 24, 2019
2 parents 16eb09d + 2072073 commit 23fd6d6
Show file tree
Hide file tree
Showing 75 changed files with 1,214 additions and 277 deletions.
3 changes: 3 additions & 0 deletions .php_cs
Expand Up @@ -11,6 +11,9 @@ return PhpCsFixer\Config::create()
'declare_strict_types' => true,
'no_alias_functions' => true,
'not_operator_with_space' => true,
'return_type_declaration' => true,
'phpdoc_to_return_type' => true,
'void_return' => true,
])
->setRiskyAllowed(true)
->setFinder($finder);
23 changes: 12 additions & 11 deletions .travis.yml
@@ -1,28 +1,29 @@
language: php

php:
- 7.2
- 7.3
- 7.4snapshot
- nightly
- 7.2
- 7.3
- 7.4snapshot
- nightly

matrix:
allow_failures:
- php: 7.4snapshot
- php: nightly
- php: 7.4snapshot
- php: nightly

cache:
directories:
- vendor
directories:
- vendor

sudo: false

install:
- travis_retry composer install
- travis_retry composer install

script:
- vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
- vendor/bin/phpstan analyze --level max -c phpstan.neon src
- if [[ "${TRAVIS_PHP_VERSION}" == "7.3" ]]; then vendor/bin/php-cs-fixer fix -v --dry-run --stop-on-violation --using-cache=no; fi
- if [[ "${TRAVIS_PHP_VERSION}" == "7.3" ]]; then vendor/bin/phpstan analyze --level max -c phpstan.neon src; fi
- vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover

after_script:
- wget https://scrutinizer-ci.com/ocular.phar
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -15,7 +15,7 @@
"symfony/yaml": "^3.2|^4.0"
},
"require-dev": {
"phpstan/phpstan": "@stable",
"phpstan/phpstan": "^0.11.16",
"phpunit/phpunit": "^7.0",
"friendsofphp/php-cs-fixer": "^2.15"
},
Expand Down
14 changes: 14 additions & 0 deletions docs/docs/changelog.md
Expand Up @@ -5,6 +5,20 @@ published_at: 2018-03-14
updated_at: 2019-07-17
---

## 0.7.0

### New Features

- Snapshotting 🤩
- Code Generation supports user defined interfaces for generated classes.
- A new `EventConsumer` base-class is provided to simplify event consumption.

### Breaking changes

- Message repositories are now expected to return the aggregate version as the `Generator` return value.
- Message repositories must now implement the `retrieveAllAfterVersion` method.
- Many things have return types now 👍

## 0.6.0

### Breaking changes
Expand Down
4 changes: 4 additions & 0 deletions docs/docs/getting-started/1-creating-an-aggregate-root.md
Expand Up @@ -26,6 +26,10 @@ class AcmeProcess implements AggregateRoot
}
```

> NOTE: The default reconstitution mechanism allows aggregates to be
> constructed without history. Use the `AggregateRootBehaviourWithRequiredHistory` trait instead.

### Aggregate Construction

The `AggregateRootBehavior` trait includes a constructor, accepting an Aggregate
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/index.md
Expand Up @@ -35,7 +35,7 @@ control over it. It allows everything from custom storage adapter to highly cus
message dispatching setups.

EventSauce puts the focus on event sourcing, not on things that happen around event
sourcing. It does not require you to follow CQRS patterns (althought you can). It does
sourcing. It does not require you to follow CQRS patterns (although you can). It does
not require you to use a command-, event-, or query-bus. By doing so, it allows
developers to use event sourcing for parts of their application more easily.

Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Expand Up @@ -12,6 +12,7 @@
<exclude>
<directory suffix="Test.php">src/</directory>
<directory suffix="Fixture.php">src/</directory>
<directory suffix=".php">src/CodeGeneration/Fixtures/</directory>
</exclude>
</whitelist>
</filter>
Expand Down
8 changes: 4 additions & 4 deletions src/AggregateRootBehaviour.php
Expand Up @@ -38,21 +38,22 @@ public function aggregateRootId(): AggregateRootId

/**
* @return int
*
* @see AggregateRoot::aggregateRootVersion
*/
public function aggregateRootVersion(): int
{
return $this->aggregateRootVersion;
}

protected function apply(object $event)
protected function apply(object $event): void
{
$parts = explode('\\', get_class($event));
$this->{'apply' . end($parts)}($event);
++$this->aggregateRootVersion;
}

protected function recordThat(object $event)
protected function recordThat(object $event): void
{
$this->apply($event);
$this->recordedEvents[] = $event;
Expand All @@ -79,7 +80,7 @@ public function releaseEvents(): array
*/
public static function reconstituteFromEvents(AggregateRootId $aggregateRootId, Generator $events): AggregateRoot
{
/** @var AggregateRootBehaviour $aggregateRoot */
/** @var AggregateRoot&static $aggregateRoot */
$aggregateRoot = new static($aggregateRootId);

/** @var object $event */
Expand All @@ -89,7 +90,6 @@ public static function reconstituteFromEvents(AggregateRootId $aggregateRootId,

$aggregateRoot->aggregateRootVersion = $events->getReturn() ?: 0;

/* @var AggregateRoot $aggregateRoot */
return $aggregateRoot;
}
}
25 changes: 25 additions & 0 deletions src/AggregateRootBehaviourWithRequiredHistory.php
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace EventSauce\EventSourcing;

use Generator;

trait AggregateRootBehaviourWithRequiredHistory
{
use AggregateRootBehaviour {
AggregateRootBehaviour::reconstituteFromEvents as private defaultAggregateRootReconstitute;
}

public static function reconstituteFromEvents(AggregateRootId $aggregateRootId, Generator $events): AggregateRoot
{
$aggregateRoot = static::defaultAggregateRootReconstitute($aggregateRootId, $events);

if (0 === $aggregateRoot->aggregateRootVersion()) {
throw new InvalidAggregateRootReconstitutionException();
}

return $aggregateRoot;
}
}
18 changes: 14 additions & 4 deletions src/AggregateRootTestCase.php
Expand Up @@ -62,7 +62,7 @@ abstract class AggregateRootTestCase extends TestCase
/**
* @before
*/
protected function setUpEventSauce()
protected function setUpEventSauce(): void
{
$className = $this->aggregateRootClassName();
$this->clock = new TestClock();
Expand All @@ -82,10 +82,20 @@ protected function setUpEventSauce()
$this->caughtException = null;
}

protected function retrieveAggregateRoot(AggregateRootId $id): object
{
return $this->repository->retrieve($id);
}

protected function persistAggregateRoot(AggregateRoot $aggregateRoot): void
{
$this->repository->persist($aggregateRoot);
}

/**
* @after
*/
protected function assertScenario()
protected function assertScenario(): void
{
// @codeCoverageIgnoreStart
if ($this->assertedScenario) {
Expand Down Expand Up @@ -177,12 +187,12 @@ protected function thenNothingShouldHaveHappened()
return $this;
}

protected function assertLastCommitEqualsEvents(object ...$events)
protected function assertLastCommitEqualsEvents(object ...$events): void
{
self::assertEquals($events, $this->messageRepository->lastCommit(), 'Events are not equal.');
}

private function assertExpectedException(Exception $expectedException = null, Exception $caughtException = null)
private function assertExpectedException(Exception $expectedException = null, Exception $caughtException = null): void
{
if ($expectedException == $caughtException) {
return;
Expand Down
15 changes: 14 additions & 1 deletion src/CodeGeneration/CodeDumper.php
Expand Up @@ -26,6 +26,7 @@ public function dump(DefinitionGroup $definitionGroup, bool $withHelpers = true,
$definitionCode = $this->dumpClasses($definitionGroup->events(), $withHelpers, $withSerialization);
$commandCode = $this->dumpClasses($definitionGroup->commands(), $withHelpers, $withSerialization);
$namespace = $definitionGroup->namespace();

$allCode = implode("\n\n", array_filter([$definitionCode, $commandCode]));

if ($withSerialization) {
Expand All @@ -46,6 +47,13 @@ public function dump(DefinitionGroup $definitionGroup, bool $withHelpers = true,
EOF;
}

/**
* @param PayloadDefinition[] $definitions
* @param bool $withHelpers
* @param bool $withSerialization
*
* @return string
*/
private function dumpClasses(array $definitions, bool $withHelpers, bool $withSerialization): string
{
$code = [];
Expand All @@ -56,12 +64,17 @@ private function dumpClasses(array $definitions, bool $withHelpers, bool $withSe

foreach ($definitions as $definition) {
$name = $definition->name();
$interfaces = $definition->interfaces();
$fields = $this->dumpFields($definition);
$constructor = $this->dumpConstructor($definition);
$methods = $this->dumpMethods($definition);
$deserializer = $this->dumpSerializationMethods($definition);
$testHelpers = $withHelpers ? $this->dumpTestHelpers($definition) : '';
$implements = $withSerialization ? ' implements SerializablePayload' : '';

if ($withSerialization) {
$interfaces[] = 'SerializablePayload';
}
$implements = empty($interfaces) ? '' : ' implements ' . implode(', ', $interfaces);

$allSections = [$fields, $constructor, $methods, $deserializer, $testHelpers];
$allSections = array_filter(array_map('rtrim', $allSections));
Expand Down
2 changes: 1 addition & 1 deletion src/CodeGeneration/CodeDumperTest.php
Expand Up @@ -13,7 +13,7 @@ class CodeDumperTest extends TestCase
* @test
* @dataProvider definitionProvider
*/
public function dumping_a_definition(DefinitionGroup $definitionGroup, string $fixtureFile)
public function dumping_a_definition(DefinitionGroup $definitionGroup, string $fixtureFile): void
{
$dumper = new CodeDumper();
$actual = $dumper->dump($definitionGroup);
Expand Down
33 changes: 27 additions & 6 deletions src/CodeGeneration/DefinitionGroup.php
Expand Up @@ -5,6 +5,8 @@
namespace EventSauce\EventSourcing\CodeGeneration;

use EventSauce\EventSourcing\PointInTime;
use OutOfBoundsException;
use function array_key_exists;

final class DefinitionGroup
{
Expand Down Expand Up @@ -67,6 +69,11 @@ final class DefinitionGroup
*/
private $typeAliases = [];

/**
* @var string[]
*/
private $interfaces = [];

public function __construct()
{
$this->typeSerializer(PointInTime::class, '{param}->toString()');
Expand All @@ -85,7 +92,7 @@ public function withNamespace(string $namespace): DefinitionGroup
return $this;
}

public function typeSerializer(string $type, string $template)
public function typeSerializer(string $type, string $template): void
{
$type = $this->resolveTypeAlias($type);

Expand All @@ -99,7 +106,7 @@ public function serializerForType($type)
return $this->typeSerializer[$type] ?? 'new {type}({param})';
}

public function typeDeserializer(string $type, string $template)
public function typeDeserializer(string $type, string $template): void
{
$type = $this->resolveTypeAlias($type);

Expand All @@ -113,7 +120,7 @@ public function deserializerForType($type)
return $this->typeDeserializer[$type] ?? 'new {type}({param})';
}

public function fieldSerializer(string $field, string $template)
public function fieldSerializer(string $field, string $template): void
{
$this->fieldSerializer[$field] = $template;
}
Expand All @@ -123,7 +130,7 @@ public function serializerForField($field)
return $this->fieldSerializer[$field] ?? null;
}

public function fieldDeserializer(string $field, string $template)
public function fieldDeserializer(string $field, string $template): void
{
$this->fieldDeserializer[$field] = $template;
}
Expand All @@ -133,13 +140,13 @@ public function deserializerForField($field)
return $this->fieldDeserializer[$field] ?? null;
}

public function fieldDefault(string $name, string $type, string $example = null)
public function fieldDefault(string $name, string $type, string $example = null): void
{
$type = $this->resolveTypeAlias($type);
$this->defaults[$name] = compact('type', 'example');
}

public function aliasType(string $alias, string $type)
public function aliasType(string $alias, string $type): void
{
$this->typeAliases[$alias] = TypeNormalizer::normalize($type);
}
Expand Down Expand Up @@ -193,4 +200,18 @@ public function namespace(): string
{
return $this->namespace;
}

public function defineInterface(string $alias, string $interfaceName): void
{
$this->interfaces[$alias] = $interfaceName;
}

public function resolveInterface(string $alias): string
{
if ( ! array_key_exists($alias, $this->interfaces)) {
throw new OutOfBoundsException("Interface not registered for alias ${alias}.");
}

return $this->interfaces[$alias];
}
}
2 changes: 1 addition & 1 deletion src/CodeGeneration/DefinitionGroupTest.php
Expand Up @@ -11,7 +11,7 @@ class DefinitionGroupTest extends TestCase
/**
* @test
*/
public function creating_a_definition_group()
public function creating_a_definition_group(): void
{
$group = DefinitionGroup::create($namespace = 'Some\\Namespace');
$this->assertEquals($namespace, $group->namespace());
Expand Down
9 changes: 9 additions & 0 deletions src/CodeGeneration/Fixtures/MarkerInterfaceStub.php
@@ -0,0 +1,9 @@
<?php


namespace EventSauce\EventSourcing\CodeGeneration\Fixtures;

interface MarkerInterfaceStub
{

}
14 changes: 14 additions & 0 deletions src/CodeGeneration/Fixtures/commands-with-interfaces.yaml
@@ -0,0 +1,14 @@
---
namespace: CommandsWithInterfaces
interfaces:
marker: EventSauce\EventSourcing\CodeGeneration\Fixtures\MarkerInterfaceStub

commands:
CommandWithInterfaceMarker:
implements: marker
AlsoCommandWithInterfaceMarker:
implements:
- marker
events:
EventWithInterfaceMarker:
implements: marker

0 comments on commit 23fd6d6

Please sign in to comment.