Skip to content

Commit

Permalink
rewrite aggregate id docs
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBadura committed Feb 1, 2024
1 parent b19329e commit 80f5c1b
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 89 deletions.
4 changes: 2 additions & 2 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ nav:
- Processor: processor.md
- Projection: projection.md
- Advanced:
- Aggregate ID: aggregate_id.md
- Normalizer: normalizer.md
- Snapshots: snapshots.md
- Upcasting: upcasting.md
Expand All @@ -94,9 +95,8 @@ nav:
- Message Decorator: message_decorator.md
- Split Stream: split_stream.md
- Time / Clock: clock.md
- Testing: testing.md
- Other / Tools:
- UUID: uuid.md
- CLI: cli.md
- Schema Migration: migration.md
- Watch Server: watch_server.md
- Tests: tests.md
210 changes: 123 additions & 87 deletions docs/pages/aggregate_id.md
Original file line number Diff line number Diff line change
@@ -1,135 +1,171 @@
# UUID
# Aggregate ID

A UUID can be generated for the `aggregateId`. There are two popular libraries that can be used:
The `aggregate id` is a unique identifier for an aggregate.
It is used to identify the aggregate in the event store.
The `aggregate` does not care how the id is generated,
since only an aggregate-wide unique string is expected in the store.

* [ramsey/uuid](https://github.com/ramsey/uuid)
* [symfony/uid](https://symfony.com/doc/current/components/uid.html)
This library provides you with a few options for generating the id.

The `aggregate` does not care how the id is generated, since only an aggregate-wide unique string is expected here.
## Uuid

The easiest way is to use an `uuid` as an aggregate ID.
For this, we have the `Uuid` class, which is a simple wrapper for the [ramsey/uuid](https://github.com/ramsey/uuid) library.

You can use it like this:

```php
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
use Patchlevel\EventSourcing\Aggregate\Uuid;
use Patchlevel\EventSourcing\Attribute\Aggregate;
use Patchlevel\EventSourcing\Attribute\Apply;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Patchlevel\EventSourcing\Attribute\Id;
use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer;

#[Aggregate('profile')]
final class Profile extends BasicAggregateRoot
{
private UuidInterface $id;
private string $name;
#[Id]
#[IdNormalizer(Uuid::class)]
private Uuid $id;
}
```

public function aggregateRootId(): string
{
return $this->id->toString();
}

public function id(): UuidInterface
{
return $this->id;
}

public function name(): string
{
return $this->name;
}
!!! note

public static function create(string $name): self
{
$id = Uuid::uuid4();

$self = new self();
$self->recordThat(new ProfileCreated($id, $name));
If you want to use snapshots, then you have to make sure that the aggregate id are normalized.
You can find how to do this [here](normalizer.md).

return $self;
}

#[Apply]
protected function applyProfileCreated(ProfileCreated $event): void
{
$this->id = $event->profileId();
$this->name = $event->name();
}
You have multiple options for generating an uuid:

```php
use Patchlevel\EventSourcing\Aggregate\Uuid;

$uuid = Uuid::v6();
$uuid = Uuid::v7();
$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.
More information about uuid versions can be found [here](https://uuid.ramsey.dev/en/stable/rfc4122.html).

## Custom ID

If you don't want to use an uuid, you can also use the custom ID implementation.
This is a value object that holds any string.

```php
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
use Patchlevel\EventSourcing\Aggregate\CustomId;
use Patchlevel\EventSourcing\Attribute\Aggregate;
use Patchlevel\EventSourcing\Attribute\Apply;
use Patchlevel\EventSourcing\Attribute\Id;
use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer;

#[Aggregate('profile')]
final class Profile extends BasicAggregateRoot
{
#[Id]
#[IdNormalizer(CustomId::class)]
private CustomId $id;
}
```

Or even better, you create your own aggregate-specific id class.
!!! note

If you want to use snapshots, then you have to make sure that the aggregate id are normalized.
You can find how to do this [here](normalizer.md).

So you can use any string as an id:

```php
use Patchlevel\EventSourcing\Aggregate\CustomId;

$id = CustomId::fromString('my-id');
```

## Implement own ID

Or even better, you create your own aggregate-specific ID class.
This allows you to ensure that the correct id is always used.
The whole thing looks like this:

```php
use Ramsey\Uuid\Uuid;
use Patchlevel\EventSourcing\Aggregate\AggregateRootId;

class ProfileId
class ProfileId implements AggregateRootId
{
private string $id;

public function __construct(string $id)
{
$this->id = $id;
}

public static function generate(): self
{
return new self(Uuid::uuid4()->toString());
private function __construct(
private readonly string $id
){
}

public function toString(): string
{
return $this->id;
}

public static function fromString(string $id): self
{
return new self($id);
}
}
```

So you can use it like this:

```php
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
use Patchlevel\EventSourcing\Attribute\Aggregate;
use Patchlevel\EventSourcing\Attribute\Apply;

use Ramsey\Uuid\UuidInterface;
use Patchlevel\EventSourcing\Attribute\Id;
use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer;

#[Aggregate('profile')]
final class Profile extends BasicAggregateRoot
{
#[Id]
#[IdNormalizer(ProfileId::class)]
private ProfileId $id;
private string $name;
}
```

public function aggregateRootId(): string
{
return $this->id->toString();
}

public function id(): ProfileId
{
return $this->id;
}

public function name(): string
{
return $this->name;
}
!!! note

public static function create(string $name): self
{
$id = ProfileId::generate();

$self = new self();
$self->recordThat(new ProfileCreated($id, $name));
If you want to use snapshots, then you have to make sure that the aggregate id are normalized.
You can find how to do this [here](normalizer.md).

return $self;
}

#[Apply]
protected function applyProfileCreated(ProfileCreated $event): void
{
$this->id = $event->profileId();
$this->name = $event->name();
}
We also offer you some traits, so that you don't have to implement the `AggregateRootId` interface yourself.
Here for the uuid:

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

class ProfileId implements AggregateRootId
{
use RamseyUuidBehaviour;
}
```

!!! note
Or for the custom id:

If you want to use snapshots, then you have to make sure that the value objects are normalized.
You can find how to do this [here](normalizer.md).
```php
use Patchlevel\EventSourcing\Aggregate\AggregateRootId;
use Patchlevel\EventSourcing\Aggregate\CustomIdBehaviour;

class ProfileId implements AggregateRootId
{
use CustomIdBehaviour;
}
```

### Learn more

* [How to create an aggregate](aggregate.md)
* [How to create an event](events.md)
* [How to test an aggregate](testing.md)
* [How to normalize value objects](normalizer.md)
File renamed without changes.

0 comments on commit 80f5c1b

Please sign in to comment.