Skip to content
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

refactor uuid implementation & add a test uuid factory #584

Merged
merged 1 commit into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions docs/pages/aggregate_id.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,12 @@ You have multiple options for generating an uuid:
```php
use Patchlevel\EventSourcing\Aggregate\Uuid;

$uuid = Uuid::v6();
$uuid = Uuid::v7();
$uuid = Uuid::generate();
$uuid = Uuid::fromString('d6e8d7a0-4b0b-4e6a-8a9a-3a0b2d9d0e4e');
```
!!! Note

We offer you the uuid versions 6 and 7, because they are the most suitable for event sourcing.
We implemented the version 7 of the uuid, because it is most suitable for event sourcing.
More information about uuid versions can be found [here](https://uuid.ramsey.dev/en/stable/rfc4122.html).

## Custom ID
Expand Down Expand Up @@ -145,11 +144,11 @@ Here for the uuid:

```php
use Patchlevel\EventSourcing\Aggregate\AggregateRootId;
use Patchlevel\EventSourcing\Aggregate\RamseyUuidBehaviour;
use Patchlevel\EventSourcing\Aggregate\RamseyUuidV7Behaviour;

class ProfileId implements AggregateRootId
{
use RamseyUuidBehaviour;
use RamseyUuidV7Behaviour;
}
```
Or for the custom id:
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ use Patchlevel\EventSourcing\Aggregate\Uuid;
use Patchlevel\EventSourcing\Repository\Repository;
use Patchlevel\EventSourcing\Subscription\Engine\SubscriptionEngine;

$hotel1 = Hotel::create(Uuid::v7(), 'HOTEL');
$hotel1 = Hotel::create(Uuid::generate(), 'HOTEL');
$hotel1->checkIn('David');
$hotel1->checkIn('Daniel');
$hotel1->checkOut('David');
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ These events are then also append to the database.
use Patchlevel\EventSourcing\Aggregate\Uuid;
use Patchlevel\EventSourcing\Repository\Repository;

$id = Uuid::v7();
$id = Uuid::generate();
$profile = Profile::create($id, 'david.badura@patchlevel.de');

/** @var Repository $repository */
Expand Down
84 changes: 82 additions & 2 deletions docs/pages/testing.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# Tests

The library is designed to be easily testable.
We provide you with a few helpers to make testing easier
and some tips on how to test your application.

## Aggregate Unit Tests

The aggregates can also be tested very well.
You can test whether certain events have been thrown
You can test whether certain events have been recorded
or whether the state is set up correctly when the aggregate is set up again via the events.

```php
Expand All @@ -23,7 +29,16 @@ final class ProfileTest extends TestCase

self::assertEquals('foo@email.com', $profile->email()->toString());
}
}
```
You can also prepare the aggregate with events to a specific state.
And then test whether the aggregate behaves as expected.

```php
use PHPUnit\Framework\TestCase;

final class ProfileTest extends TestCase
{
public function testChangeName(): void
{
$id = ProfileId::generate();
Expand All @@ -44,4 +59,69 @@ final class ProfileTest extends TestCase
self::assertEquals('bar@email.com', $profile->email()->toString());
}
}
```
```
## Tests with DateTime

You should not instantiate the `DateTimeImmutable` directly in the aggregate.
Instead, you should pass a `Clock` to the aggregate and use this to get the current time.
This allows you to test the aggregate with a fixed time.

```php
use Patchlevel\EventSourcing\Clock\FrozenClock;
use PHPUnit\Framework\TestCase;

final class ProfileTest extends TestCase
{
public function testCreateProfile(): void
{
$clock = new FrozenClock(new DateTimeImmutable('2021-01-01 00:00:00'));

$profile = Profile::createProfile(
ProfileId::generate(),
Email::fromString('info@patchlevel.de'),
$clock,
);

$clock->sleep(10);

$profile->changeEmail(Email::fromString('info@patchlevel.de'));
}
}
```
!!! note

You can find out more about the clock [here](clock).

!!! tip

You can use the FreezeClock in you integration tests to test the time-based behavior of your application.

## Tests with UUID

Uuids are randomly generated and can be a problem in tests.
If you want deterministic tests, you can use the `IncrementalRamseyUuidFactory` from the library.

```php
use Patchlevel\EventSourcing\Test\IncrementalRamseyUuidFactory;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\Uuid;

final class ProfileTest extends TestCase
{
public function setUp(): void
{
Uuid::setFactory(new IncrementalRamseyUuidFactory());
}

public function testCreateProfile(): void
{
$id1 = ProfileId::generate(); // 10000000-7000-0000-0000-000000000001
$id2 = ProfileId::generate(); // 10000000-7000-0000-0000-000000000002
}
}
```
!!! warning

The `IncrementalRamseyUuidFactory` is only for testing purposes
and supports only the version 7 what is used by the library.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

trait RamseyUuidBehaviour
trait RamseyUuidV7Behaviour
{
public function __construct(
private readonly UuidInterface $id,
Expand All @@ -24,12 +24,7 @@ public function toString(): string
return $this->id->toString();
}

public static function v6(): self
{
return new self(Uuid::uuid6());
}

public static function v7(): self
public static function generate(): self
{
return new self(Uuid::uuid7());
}
Expand Down
2 changes: 1 addition & 1 deletion src/Aggregate/Uuid.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@

final class Uuid implements AggregateRootId
{
use RamseyUuidBehaviour;
use RamseyUuidV7Behaviour;
}
37 changes: 37 additions & 0 deletions src/Test/IncrementalRamseyUuidFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Test;

use DateTimeInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidFactory;
use Ramsey\Uuid\UuidInterface;

use function sprintf;
use function str_pad;

use const STR_PAD_LEFT;

final class IncrementalRamseyUuidFactory extends UuidFactory
{
private int $counter = 0;

public function uuid7(DateTimeInterface|null $dateTime = null): UuidInterface
{
$number = ++$this->counter;

$string = sprintf(
'10000000-7000-0000-0000-%s',
str_pad((string)$number, 12, '0', STR_PAD_LEFT),
);

return Uuid::fromString($string);
}

public function reset(): void
{
$this->counter = 0;
}
}
4 changes: 2 additions & 2 deletions tests/Benchmark/BasicImplementation/ProfileId.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
namespace Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation;

use Patchlevel\EventSourcing\Aggregate\AggregateRootId;
use Patchlevel\EventSourcing\Aggregate\RamseyUuidBehaviour;
use Patchlevel\EventSourcing\Aggregate\RamseyUuidV7Behaviour;

final class ProfileId implements AggregateRootId
{
use RamseyUuidBehaviour;
use RamseyUuidV7Behaviour;
}
12 changes: 6 additions & 6 deletions tests/Benchmark/PersonalDataBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ public function setUp(): void

$schemaDirector->create();

$this->singleEventId = ProfileId::v7();
$this->singleEventId = ProfileId::generate();
$profile = Profile::create($this->singleEventId, 'Peter');
$this->repository->save($profile);

$this->multipleEventsId = ProfileId::v7();
$this->multipleEventsId = ProfileId::generate();
$profile = Profile::create($this->multipleEventsId, 'Peter', 'info@patchlevel.de');

for ($i = 0; $i < 10_000; $i++) {
Expand All @@ -87,14 +87,14 @@ public function benchLoad10000Events(): void
#[Bench\Revs(10)]
public function benchSave1Event(): void
{
$profile = Profile::create(ProfileId::v7(), 'Peter', 'info@patchlevel.de');
$profile = Profile::create(ProfileId::generate(), 'Peter', 'info@patchlevel.de');
$this->repository->save($profile);
}

#[Bench\Revs(10)]
public function benchSave10000Events(): void
{
$profile = Profile::create(ProfileId::v7(), 'Peter', 'info@patchlevel.de');
$profile = Profile::create(ProfileId::generate(), 'Peter', 'info@patchlevel.de');

for ($i = 1; $i < 10_000; $i++) {
$profile->changeEmail('info@patchlevel.de');
Expand All @@ -107,7 +107,7 @@ public function benchSave10000Events(): void
public function benchSave10000Aggregates(): void
{
for ($i = 1; $i < 10_000; $i++) {
$profile = Profile::create(ProfileId::v7(), 'Peter', 'info@patchlevel.de');
$profile = Profile::create(ProfileId::generate(), 'Peter', 'info@patchlevel.de');
$this->repository->save($profile);
}
}
Expand All @@ -117,7 +117,7 @@ public function benchSave10000AggregatesTransaction(): void
{
$this->store->transactional(function (): void {
for ($i = 1; $i < 10_000; $i++) {
$profile = Profile::create(ProfileId::v7(), 'Peter', 'info@patchlevel.de');
$profile = Profile::create(ProfileId::generate(), 'Peter', 'info@patchlevel.de');
$this->repository->save($profile);
}
});
Expand Down
12 changes: 6 additions & 6 deletions tests/Benchmark/SimpleSetupBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public function setUp(): void

$schemaDirector->create();

$this->singleEventId = ProfileId::v7();
$this->singleEventId = ProfileId::generate();
$profile = Profile::create($this->singleEventId, 'Peter');
$this->repository->save($profile);

$this->multipleEventsId = ProfileId::v7();
$this->multipleEventsId = ProfileId::generate();
$profile = Profile::create($this->multipleEventsId, 'Peter');

for ($i = 0; $i < 10_000; $i++) {
Expand All @@ -72,14 +72,14 @@ public function benchLoad10000Events(): void
#[Bench\Revs(10)]
public function benchSave1Event(): void
{
$profile = Profile::create(ProfileId::v7(), 'Peter');
$profile = Profile::create(ProfileId::generate(), 'Peter');
$this->repository->save($profile);
}

#[Bench\Revs(10)]
public function benchSave10000Events(): void
{
$profile = Profile::create(ProfileId::v7(), 'Peter');
$profile = Profile::create(ProfileId::generate(), 'Peter');

for ($i = 1; $i < 10_000; $i++) {
$profile->changeName('Peter');
Expand All @@ -92,7 +92,7 @@ public function benchSave10000Events(): void
public function benchSave10000Aggregates(): void
{
for ($i = 1; $i < 10_000; $i++) {
$profile = Profile::create(ProfileId::v7(), 'Peter');
$profile = Profile::create(ProfileId::generate(), 'Peter');
$this->repository->save($profile);
}
}
Expand All @@ -102,7 +102,7 @@ public function benchSave10000AggregatesTransaction(): void
{
$this->store->transactional(function (): void {
for ($i = 1; $i < 10_000; $i++) {
$profile = Profile::create(ProfileId::v7(), 'Peter');
$profile = Profile::create(ProfileId::generate(), 'Peter');
$this->repository->save($profile);
}
});
Expand Down
2 changes: 1 addition & 1 deletion tests/Benchmark/SnapshotsBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function setUp(): void

$schemaDirector->create();

$this->id = ProfileId::v7();
$this->id = ProfileId::generate();
$profile = Profile::create($this->id, 'Peter');

for ($i = 0; $i < 10_000; $i++) {
Expand Down
4 changes: 2 additions & 2 deletions tests/Benchmark/SplitStreamBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function setUp(): void

$schemaDirector->create();

$this->id = ProfileId::v7();
$this->id = ProfileId::generate();
}

public function provideData(): void
Expand Down Expand Up @@ -84,7 +84,7 @@ public function benchLoad10000Events(): void
#[Bench\Revs(10)]
public function benchSave10000Events(): void
{
$profile = Profile::create(ProfileId::v7(), 'Peter');
$profile = Profile::create(ProfileId::generate(), 'Peter');

for ($i = 0; $i < 10_000; $i++) {
$profile->changeName(sprintf('Peter %d', $i));
Expand Down
2 changes: 1 addition & 1 deletion tests/Benchmark/SubscriptionEngineBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function setUp(): void

$schemaDirector->create();

$this->id = ProfileId::v7();
$this->id = ProfileId::generate();

$profile = Profile::create($this->id, 'Peter');

Expand Down
2 changes: 1 addition & 1 deletion tests/Benchmark/blackfire.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

$store->transactional(static function () use ($repository): void {
for ($i = 0; $i < 10_000; $i++) {
$id = ProfileId::v7();
$id = ProfileId::generate();
$profile = Profile::create($id, 'Peter');

for ($j = 0; $j < 10; $j++) {
Expand Down
4 changes: 2 additions & 2 deletions tests/Integration/BankAccountSplitStream/AccountId.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
namespace Patchlevel\EventSourcing\Tests\Integration\BankAccountSplitStream;

use Patchlevel\EventSourcing\Aggregate\AggregateRootId;
use Patchlevel\EventSourcing\Aggregate\RamseyUuidBehaviour;
use Patchlevel\EventSourcing\Aggregate\RamseyUuidV7Behaviour;

final class AccountId implements AggregateRootId
{
use RamseyUuidBehaviour;
use RamseyUuidV7Behaviour;
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function testSuccessful(): void
$engine->setup();
$engine->boot();

$bankAccountId = AccountId::v7();
$bankAccountId = AccountId::generate();
$bankAccount = BankAccount::create($bankAccountId, 'John');
$bankAccount->addBalance(100);
$bankAccount->addBalance(500);
Expand Down
Loading
Loading