Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBadura committed Dec 16, 2024
1 parent 9217fa2 commit 1f14d22
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 36 deletions.
12 changes: 8 additions & 4 deletions docs/pages/aggregate.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,14 @@ In order to change the state of the aggregates afterwards, only further events h
As example we can add a `NameChanged` event:

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

#[Event('profile.name_changed')]
final class NameChanged
{
public function __construct(
public readonly Uuid $profileId,
public readonly string $name,
) {
}
Expand Down Expand Up @@ -217,7 +219,7 @@ final class Profile extends BasicAggregateRoot

public function changeName(string $name): void
{
$this->recordThat(new NameChanged($name));
$this->recordThat(new NameChanged($this->id, $name));
}

#[Apply]
Expand Down Expand Up @@ -538,7 +540,7 @@ final class Profile extends BasicAggregateRoot

public function changeName(Name $name): void
{
$this->recordThat(new NameChanged($name));
$this->recordThat(new NameChanged($this->id, $name));
}

#[Apply]
Expand All @@ -552,12 +554,14 @@ In order for the whole thing to work, we still have to adapt our `NameChanged` e
since we only expected a string before but now passed a `Name` value object.

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

#[Event('profile.name_changed')]
final class NameChanged
{
public function __construct(
public readonly Uuid $profileId,
#[NameNormalizer]
public readonly Name $name,
) {
Expand Down Expand Up @@ -604,13 +608,13 @@ final class Hotel extends BasicAggregateRoot
throw new NoPlaceException($name);
}

$this->recordThat(new RoomBocked($name));
$this->recordThat(new RoomBocked($this->id, $name));

if ($this->people !== self::SIZE) {
return;
}

$this->recordThat(new FullyBooked());
$this->recordThat(new FullyBooked($this->id));
}

#[Apply]
Expand Down
1 change: 1 addition & 0 deletions docs/pages/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use Patchlevel\EventSourcing\Attribute\Event;
#[Event(name: 'profile.registered', aliases: ['profile.created'])]
final class ProfileRegistered
{
// ...
}
```
When saving, the name will always be used. However, when loading, aliases will also be taken into account.
Expand Down
89 changes: 58 additions & 31 deletions docs/pages/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ final class HotelCreated
A guest can check in by `name`:

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

#[Event('hotel.guest_checked_in')]
final class GuestIsCheckedIn
{
public function __construct(
public readonly Uuid $hotelId,
public readonly string $guestName,
) {
}
Expand All @@ -40,12 +42,14 @@ final class GuestIsCheckedIn
And also check out again:

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

#[Event('hotel.guest_checked_out')]
final class GuestIsCheckedOut
{
public function __construct(
public readonly Uuid $hotelId,
public readonly string $guestName,
) {
}
Expand Down Expand Up @@ -102,19 +106,19 @@ final class Hotel extends BasicAggregateRoot
public function checkIn(string $guestName): void
{
if (in_array($guestName, $this->guests, true)) {
throw new GuestHasAlreadyCheckedIn($guestName);
throw new GuestHasAlreadyCheckedIn($this->id, $guestName);
}

$this->recordThat(new GuestIsCheckedIn($guestName));
$this->recordThat(new GuestIsCheckedIn($this->id, $guestName));
}

public function checkOut(string $guestName): void
{
if (!in_array($guestName, $this->guests, true)) {
throw new IsNotAGuest($guestName);
throw new IsNotAGuest($this->id, $guestName);
}

$this->recordThat(new GuestIsCheckedOut($guestName));
$this->recordThat(new GuestIsCheckedOut($this->id, $guestName));
}

#[Apply]
Expand Down Expand Up @@ -162,57 +166,80 @@ use Patchlevel\EventSourcing\Attribute\Subscribe;
use Patchlevel\EventSourcing\Attribute\Teardown;
use Patchlevel\EventSourcing\Subscription\Subscriber\SubscriberUtil;

#[Projector('hotel')]
final class HotelProjector
/**
* @psalm-type GuestData = array{
* guest_name: string,
* hotel_id: string,
* check_in_date: string,
* check_out_date: string|null
* }
*/
#[Projector('guests')]
final class GuestProjection
{
use SubscriberUtil;

public function __construct(
private readonly Connection $db,
private Connection $db,
) {
}

/** @return list<array{id: string, name: string, guests: int}> */
public function getHotels(): array
/** @return list<GuestData> */
public function findGuestsByHotelId(Uuid $hotelId): array
{
return $this->db->fetchAllAssociative("SELECT id, name, guests FROM {$this->table()};");
return $this->db->createQueryBuilder()
->select('*')
->from($this->table())
->where('hotel_id = :hotel_id')
->setParameter('hotel_id', $hotelId->toString())
->fetchAllAssociative();
}

#[Subscribe(HotelCreated::class)]
public function handleHotelCreated(HotelCreated $event, Uuid $aggregateId): void
{
#[Subscribe(GuestIsCheckedIn::class)]
public function onGuestIsCheckedIn(
GuestIsCheckedIn $event,
DateTimeImmutable $recordedOn,
): void {
$this->db->insert(
$this->table(),
[
'id' => $aggregateId->toString(),
'name' => $event->hotelName,
'guests' => 0,
'hotel_id' => $event->hotelId->toString(),
'guest_name' => $event->guestName,
'check_in_date' => $recordedOn->format('Y-m-d H:i:s'),
'check_out_date' => null,
],
);
}

#[Subscribe(GuestIsCheckedIn::class)]
public function handleGuestIsCheckedIn(Uuid $aggregateId): void
{
$this->db->executeStatement(
"UPDATE {$this->table()} SET guests = guests + 1 WHERE id = ?;",
[$aggregateId->toString()],
);
}

#[Subscribe(GuestIsCheckedOut::class)]
public function handleGuestIsCheckedOut(Uuid $aggregateId): void
{
$this->db->executeStatement(
"UPDATE {$this->table()} SET guests = guests - 1 WHERE id = ?;",
[$aggregateId->toString()],
public function onGuestIsCheckedOut(
GuestIsCheckedOut $event,
DateTimeImmutable $recordedOn,
): void {
$this->db->update(
$this->table(),
[
'check_out_date' => $recordedOn->format('Y-m-d H:i:s'),
],
[
'hotel_id' => $event->hotelId->toString(),
'guest_name' => $event->guestName,
'check_out_date' => null,
],
);
}

#[Setup]
public function create(): void
{
$this->db->executeStatement("CREATE TABLE IF NOT EXISTS {$this->table()} (id VARCHAR PRIMARY KEY, name VARCHAR, guests INTEGER);");
$this->db->executeStatement(
"CREATE TABLE {$this->table()} (
hotel_id VARCHAR(36) NOT NULL,
guest_name VARCHAR(255) NOT NULL,
check_in_date TIMESTAMP NOT NULL,
check_out_date TIMESTAMP NULL
);",
);
}

#[Teardown]
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/subscription.md
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ At this step, you must process all the data.
The `rollbackBatch` method is called when an error occurs and the batching needs to be aborted.
Here, you can respond to the error and potentially perform a database rollback.

The method `forceCommit` is called after each handled event,
The method `forceCommit` is called after each handled event,
and you can decide whether the batch commit process should start now.
This helps to determine the batch size and thus avoid memory overflow.

Expand Down

0 comments on commit 1f14d22

Please sign in to comment.