Skip to content

Commit fef4d56

Browse files
committed
update docs
1 parent 7b70aa1 commit fef4d56

1 file changed

Lines changed: 70 additions & 86 deletions

File tree

docs/pages/identifier.md

Lines changed: 70 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,130 @@
11
# Identifier
22

3-
The `aggregate id` is a unique identifier for an aggregate.
4-
It is used to identify the aggregate in the event store.
5-
The `aggregate` does not care how the id is generated,
6-
since only an aggregate-wide unique string is expected in the store.
3+
Identifiers are small, immutable value objects that represent IDs across the library in a type‑safe and consistent way. They are primarily used for aggregate root IDs, but can also be applied anywhere a stable string identifier is required (snapshots, repositories, your own domain objects, etc.).
74

8-
This library provides you with a few options for generating the id.
5+
All identifiers must implement `Patchlevel\EventSourcing\Identifier\Identifier` and provide a stable string representation for storage and serialization.
96

10-
!!! warning
11-
12-
Performance reasons, the default configuration of the store require an uuid string for `aggregate id`.
13-
But technically, for the library, it can be any string.
14-
If you want to use a custom id, you have to change the `aggregate_id_type` in the [store](store.md) configuration.
15-
16-
## Uuid
17-
18-
The easiest way is to use an `uuid` as an aggregate ID.
19-
For this, we have the `Uuid` class, which is a simple wrapper for the [ramsey/uuid](https://github.com/ramsey/uuid) library.
20-
21-
You can use it like this:
7+
## Interface
228

239
```php
24-
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
25-
use Patchlevel\EventSourcing\Attribute\Aggregate;
26-
use Patchlevel\EventSourcing\Attribute\Id;
27-
use Patchlevel\EventSourcing\Identifier\Uuid;
10+
use Patchlevel\EventSourcing\Identifier\Identifier;
2811

29-
#[Aggregate('profile')]
30-
final class Profile extends BasicAggregateRoot
12+
interface Identifier
3113
{
32-
#[Id]
33-
private Uuid $id;
14+
public function toString(): string;
15+
16+
public static function fromString(string $id): static;
3417
}
3518
```
36-
You have multiple options for generating an uuid:
19+
20+
The string value returned by `toString()` is what gets persisted (e.g. in the event store) and serialized. `fromString()` reconstructs the value object from that string.
21+
22+
## Built‑in identifiers
23+
24+
We provide two ready‑to‑use implementations:
25+
26+
### `Uuid`
27+
28+
`Uuid` wraps [ramsey/uuid](https://github.com/ramsey/uuid) and uses UUID v7 by default, which is a good fit for event‑sourcing.
3729

3830
```php
3931
use Patchlevel\EventSourcing\Identifier\Uuid;
4032

4133
$uuid = Uuid::generate();
4234
$uuid = Uuid::fromString('d6e8d7a0-4b0b-4e6a-8a9a-3a0b2d9d0e4e');
4335
```
44-
!!! Note
4536

46-
We implemented the version 7 of the uuid, because it is most suitable for event sourcing.
47-
More information about uuid versions can be found [here](https://uuid.ramsey.dev/en/stable/rfc4122.html).
48-
49-
## Custom ID
37+
!!! note
38+
39+
UUID v7 provides k‑sortable identifiers that work well with append‑only streams and database indexes. See the ramsey docs for details.
5040

51-
If you don't want to use an uuid, you can also use the custom ID implementation.
52-
This is a value object that holds any string.
41+
### `CustomId`
42+
43+
`CustomId` is a minimal string‑backed identifier. Use it if you want full control over the string format or if your IDs are provided by an external system.
5344

5445
```php
55-
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
56-
use Patchlevel\EventSourcing\Attribute\Aggregate;
57-
use Patchlevel\EventSourcing\Attribute\Id;
5846
use Patchlevel\EventSourcing\Identifier\CustomId;
5947

60-
#[Aggregate('profile')]
61-
final class Profile extends BasicAggregateRoot
62-
{
63-
#[Id]
64-
private CustomId $id;
65-
}
48+
$id = CustomId::fromString('my-id');
6649
```
67-
!!! warning
6850

69-
If you want to use a custom id that is not an uuid,
70-
you need to change the `aggregate_id_type` to `string` in the store configuration.
71-
More information can be found [here](store.md).
72-
73-
So you can use any string as an id:
51+
## Domain‑specific identifiers
52+
53+
For better domain modeling, define your own identifier types by implementing `Identifier` and encapsulating creation rules and validation. Two traits are available to make this easy:
54+
55+
- `RamseyUuidV7Behaviour` for UUID v7 identifiers
56+
- `CustomIdBehaviour` for string‑backed identifiers
7457

7558
```php
76-
use Patchlevel\EventSourcing\Identifier\CustomId;
59+
use Patchlevel\EventSourcing\Identifier\Identifier;
60+
use Patchlevel\EventSourcing\Identifier\RamseyUuidV7Behaviour;
7761

78-
$id = CustomId::fromString('my-id');
62+
final class ProfileId implements Identifier
63+
{
64+
use RamseyUuidV7Behaviour; // adds generate(), fromString(), toString()
65+
}
7966
```
80-
## Implement own ID
8167

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

8670
```php
8771
use Patchlevel\EventSourcing\Identifier\Identifier;
72+
use Patchlevel\EventSourcing\Identifier\CustomIdBehaviour;
8873

89-
class ProfileId implements Identifier
74+
final class OrderNumber implements Identifier
9075
{
91-
private function __construct(
92-
private readonly string $id,
93-
) {
94-
}
95-
96-
public function toString(): string
97-
{
98-
return $this->id;
99-
}
100-
101-
public static function fromString(string $id): static
102-
{
103-
return new self($id);
104-
}
76+
use CustomIdBehaviour; // simple string‑backed identifier
10577
}
10678
```
107-
So you can use it like this:
79+
80+
## Using identifiers with aggregates
81+
82+
Aggregates expose and persist their IDs as `Identifier` instances. You can choose the concrete implementation that fits your domain.
10883

10984
```php
11085
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
11186
use Patchlevel\EventSourcing\Attribute\Aggregate;
11287
use Patchlevel\EventSourcing\Attribute\Id;
88+
use Patchlevel\EventSourcing\Identifier\Uuid;
11389

11490
#[Aggregate('profile')]
11591
final class Profile extends BasicAggregateRoot
11692
{
11793
#[Id]
118-
private ProfileId $id;
94+
private Uuid $id;
11995
}
12096
```
121-
We also offer you some traits, so that you don't have to implement the `AggregateRootId` interface yourself.
122-
Here for the uuid:
97+
98+
Or use your domain‑specific identifier:
12399

124100
```php
125-
use Patchlevel\EventSourcing\Identifier\Identifier;
126-
use Patchlevel\EventSourcing\Identifier\RamseyUuidV7Behaviour;
101+
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
102+
use Patchlevel\EventSourcing\Attribute\Aggregate;
103+
use Patchlevel\EventSourcing\Attribute\Id;
127104

128-
class ProfileId implements Identifier
105+
#[Aggregate('profile')]
106+
final class Profile extends BasicAggregateRoot
129107
{
130-
use RamseyUuidV7Behaviour;
108+
#[Id]
109+
private ProfileId $id;
131110
}
132111
```
133-
Or for the custom id:
134112

135-
```php
136-
use Patchlevel\EventSourcing\Identifier\CustomIdBehaviour;
137-
use Patchlevel\EventSourcing\Identifier\Identifier;
113+
## Serialization and normalization
114+
115+
Identifiers integrate with the serializer via `IdNormalizer`. The `Identifier` interface is annotated so that instances are automatically normalized to strings and denormalized back to the correct class.
116+
117+
This means you can safely use identifier types in command payloads, events, or snapshots without writing custom normalizers.
118+
119+
## Testing
120+
121+
For deterministic tests involving UUIDs, use `FakeRamseyUuidFactory` to generate predictable UUID v7 values.
122+
123+
## Notes
124+
125+
- Identifiers are stored as strings in the backing store, but you should always use concrete identifier types in your domain code for type‑safety and clarity.
126+
- You can use identifiers beyond aggregates wherever an opaque, stable ID is needed.
138127

139-
class ProfileId implements Identifier
140-
{
141-
use CustomIdBehaviour;
142-
}
143-
```
144128
### Learn more
145129

146130
* [How to create an aggregate](aggregate.md)

0 commit comments

Comments
 (0)