Skip to content
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/aggregate-id.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ since only an aggregate-wide unique string is expected in the store.
This library provides you with a few options for generating the id.

:::warning
Performance reasons, the default configuration of the store require an uuid string for `aggregate id`.
For performance reasons, the default configuration of the store requires an uuid string for the `aggregate id`.
But technically, for the library, it can be any string.
If you want to use a custom id, you have to change the `aggregate_id_type` in the [store](store.md) configuration.
:::
Expand Down Expand Up @@ -43,7 +43,7 @@ $uuid = Uuid::fromString('d6e8d7a0-4b0b-4e6a-8a9a-3a0b2d9d0e4e');
```
:::note
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).
More information about [uuid versions](https://uuid.ramsey.dev/en/stable/rfc4122.html) can be found in the ramsey/uuid documentation.
:::

## Custom ID
Expand All @@ -66,8 +66,7 @@ final class Profile extends BasicAggregateRoot
```
:::warning
If you want to use a custom id that is not an uuid,
you need to change the `aggregate_id_type` to `string` in the store configuration.
More information can be found [here](store.md).
you need to change the `aggregate_id_type` to `string` in the [store](store.md) configuration.
:::

So you can use any string as an id:
Expand Down Expand Up @@ -141,7 +140,7 @@ class ProfileId implements AggregateRootId
use CustomIdBehaviour;
}
```
### Learn more
## Learn more

* [How to create an aggregate](aggregate.md)
* [How to create an event](events.md)
Expand Down
30 changes: 15 additions & 15 deletions docs/aggregate.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ This means it is always possible to build the current state again from the event

:::note
The term aggregate itself comes from DDD and has nothing to do with event sourcing and can be used independently as a pattern.
You can find out more about Aggregates [here](https://martinfowler.com/bliki/DDD_Aggregate.html).
You can find out more about aggregates in [Martin Fowler's article about the DDD Aggregate pattern](https://martinfowler.com/bliki/DDD_Aggregate.html).
:::

An aggregate must fulfill a few points so that we can use it in event-sourcing:

* It must implement the `AggregateRoot` interface.
* It needs a unique identifier.
* It needs to provide the current playhead.
* It must make changes to his state available as events.
* It must make changes to its state available as events.
* And rebuild/catchup its state from the events.

We can implement this ourselves, or use the `BasicAggregateRoot` implementation that already brings everything with it.
Expand Down Expand Up @@ -49,7 +49,7 @@ The aggregate is not yet finished and has only been built to the point that you
:::

:::tip
Find out more about aggregate IDs [here](aggregate-id.md).
Find out more about [aggregate IDs](aggregate-id.md).
:::

We use a so-called named constructor here to create an object of the AggregateRoot.
Expand Down Expand Up @@ -108,7 +108,7 @@ final class ProfileRegistered
}
```
:::note
You can find out more about events [here](events.md).
You can find out more about [events](events.md).
:::

After we have defined the event, we have to adapt the profile aggregate:
Expand Down Expand Up @@ -262,7 +262,7 @@ Our aggregate can now be changed and saved.
:::

:::note
You can read more about Repository [here](repository.md).
You can read more about the [repository](repository.md).
:::

Here the aggregate is loaded from the `repository` by fetching all events from the database.
Expand Down Expand Up @@ -313,7 +313,7 @@ if you define the event types in the method using a union type.
Sometimes you have events that do not change the state of the aggregate itself,
but are still recorded for the future or to subscribe for processor and projection.
So that you are not forced to write an apply method for it,
you can suppress the missing apply exceptions these events with the `SuppressMissingApply` attribute.
you can suppress the missing apply exceptions for these events with the `SuppressMissingApply` attribute.

```php
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
Expand Down Expand Up @@ -366,8 +366,8 @@ When all events are suppressed, debugging becomes more difficult if you forget a
## Shared apply context

When working with [micro-aggregates](aggregate.md#micro-aggregates),
its common that events are applied by different aggregates.
As a result, an aggregate may receive events it does not handle, which can lead to multiple missing apply warnings.
it's common that events are applied by different aggregates.
As a result, an aggregate may receive events it does not handle, which can lead to multiple "missing apply" warnings.

The `SharedApplyContext` attribute allows you to declare that several aggregates share the same apply context.
With this configuration, a missing apply is only reported if none of the shared aggregates handle the event.
Expand Down Expand Up @@ -435,7 +435,7 @@ final class GuestList extends BasicAggregateRoot
}
```
:::tip
You can find more about splitting aggregates [here](aggregate.md#splitting-aggregates).
You can find more about [splitting aggregates](aggregate.md#splitting-aggregates).
:::

## Business rules
Expand Down Expand Up @@ -463,7 +463,7 @@ final class Profile extends BasicAggregateRoot
public function changeName(string $name): void
{
if (strlen($name) < 3) {
throw new NameIsToShortException($name);
throw new NameIsTooShortException($name);
}

$this->recordThat(new NameChanged($name));
Expand Down Expand Up @@ -493,7 +493,7 @@ final class Name
public function __construct(private string $value)
{
if (strlen($value) < 3) {
throw new NameIsToShortException($value);
throw new NameIsTooShortException($value);
}
}

Expand Down Expand Up @@ -568,7 +568,7 @@ So the payload must be serializable and unserializable as json.
:::

:::note
You can find out more about normalizer [here](normalizer.md).
You can find out more about [normalizer](normalizer.md).
:::

There are also cases where business rules have to be defined depending on the aggregate state.
Expand Down Expand Up @@ -623,7 +623,7 @@ final class Hotel extends BasicAggregateRoot
An aggregate should always be deterministic. In other words, whenever I execute methods on the aggregate,
I always get the same result. This also makes testing much easier.

But that often doesn't seem to be possible, e.g. if you want to save a createAt date.
But that often doesn't seem to be possible, e.g. if you want to save a createdAt date.
But you can pass this information by yourself.

```php
Expand Down Expand Up @@ -683,7 +683,7 @@ Now you can pass the `SystemClock` to determine the current time.
Or for test purposes the `FrozenClock`, which always returns the same time.

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

## Splitting Aggregates
Expand Down Expand Up @@ -834,7 +834,7 @@ The apply method must be public, otherwise the root aggregate cannot call it.
:::

:::note
Supress missing apply methods need to be defined in the root aggregate.
Suppressing missing apply methods needs to be defined in the root aggregate.
:::

And the `Order` aggregate root looks like this:
Expand Down
5 changes: 3 additions & 2 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ To manage your subscriptions there are the following cli commands.

* SubscriptionBootCommand: `event-sourcing:subscription:boot`
* SubscriptionPauseCommand: `event-sourcing:subscription:pause`
* SubscriptionReactiveCommand: `event-sourcing:subscription:reactive`
* SubscriptionReactivateCommand: `event-sourcing:subscription:reactivate`
* SubscriptionRefreshCommand: `event-sourcing:subscription:refresh`
* SubscriptionRemoveCommand: `event-sourcing:subscription:remove`
* SubscriptionRunCommand: `event-sourcing:subscription:run`
* SubscriptionSetupCommand: `event-sourcing:subscription:setup`
* SubscriptionStatusCommand: `event-sourcing:subscription:status`
* SubscriptionTeardownCommand: `event-sourcing:subscription:teardown`

:::note
You can find out more about subscriptions [here](subscription.md).
You can find out more about [subscriptions](subscription.md).
:::

## Inspector commands
Expand Down
6 changes: 3 additions & 3 deletions docs/clock.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

We are using the clock to get the current datetime. This is needed to create the `recorded_on` datetime for the event stream.
We have two implementations of the clock, one for the production and one for the tests.
But you can also create your own implementation that is PSR-20 compatible.
For more information see [here](https://github.com/php-fig/fig-standards/blob/master/proposed/clock.md).
But you can also create your own implementation that is compatible with the
[PSR-20 clock specification](https://www.php-fig.org/psr/psr-20/).

## SystemClock

Expand Down Expand Up @@ -62,7 +62,7 @@ $clock = new FrozenClock($firstDate);
$clock->sleep(10); // sleep 10 seconds
```
:::note
The instance of the frozen datetime will be cloned internally, so the it's not the same instance but equals.
The instance of the frozen datetime will be cloned internally, so it's not the same instance but equal.
:::

## Learn more
Expand Down
12 changes: 4 additions & 8 deletions docs/command-bus.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ final class Profile extends BasicAggregateRoot
}
```
:::tip
You can find more information about aggregates [here](aggregate.md).
You can find more information about [aggregates](aggregate.md).
:::

#### Update Aggregate
Expand Down Expand Up @@ -178,10 +178,6 @@ final class Profile extends BasicAggregateRoot
#[Handle]
public function changeName(ChangeProfileName $command): void
{
if (!$nameValidator($command->name)) {
throw new InvalidArgument();
}
Comment on lines -181 to -183

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the removal of this part?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is out of scope for this area, and service injection comes later.


$this->recordThat(new NameChanged($command->name));
}

Expand All @@ -197,7 +193,7 @@ you can use the [Auto Initialize](aggregate.md#auto-initialize) feature.

You can inject services into aggregate handler methods.
Starting with the second parameter, it automatically tries to inject the service using a service locator.
Standard, it uses the fully qualified class name from the parameter type hint to find the service.
By default, it uses the fully qualified class name from the parameter type hint to find the service.

```php
use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
Expand Down Expand Up @@ -296,7 +292,7 @@ use Patchlevel\EventSourcing\CommandBus\CommandBus;
use Patchlevel\EventSourcing\CommandBus\InstantRetryCommandBus;
use Patchlevel\EventSourcing\Repository\AggregateOutdated;

/** @var CommandBus $store */
/** @var CommandBus $commandBus */
$commandBus = new InstantRetryCommandBus(
$commandBus,
Comment on lines +295 to 297

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** @var CommandBus $commandBus */
$commandBus = new InstantRetryCommandBus(
$commandBus,
/** @var CommandBus $innerCommandBus */
$innerCommandBus
$commandBus = new InstantRetryCommandBus(
$innerCommandBus,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code is not valid

3, // maximum number of retries, default is 3
Expand Down Expand Up @@ -339,7 +335,7 @@ There are different types of providers that you can use to register handlers.

### Service Handler Provider

The classically way to handle commands is to use services.
The classic way to handle commands is to use services.
The `ServiceHandlerProvider` is used to handle commands by invoking methods on services.

```php
Expand Down
4 changes: 2 additions & 2 deletions docs/event-bus.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ $listenerProvider = new class implements ListenerProvider {
};
```
:::tip
You can use `$listenerDiscriptor->name()` to get the name of the listener.
You can use `$listenerDescriptor->name()` to get the name of the listener.
:::

## Listener
Expand Down Expand Up @@ -142,4 +142,4 @@ You can't use the `Subscribe` attribute with the psr-14 event bus.
* [How to use events](events.md)
* [How to use the subscription engine](subscription.md)
* [How to use repositories](repository.md)
* [How to use decorate messages](message-decorator.md)
* [How to decorate messages](message-decorator.md)
2 changes: 1 addition & 1 deletion docs/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ and so you don't have to specify them. If you want to configure the Normalizer,
:::

:::note
You can find out more about normalizer [here](normalizer.md).
You can find out more about [normalizer](normalizer.md).
:::

## Event Registry
Expand Down
22 changes: 11 additions & 11 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ We keep the example small, so we can only create hotels and let guests check in

First we define the events that happen in our system.

A hotel can be created with a `name` and a `id`:
A hotel can be created with a `name` and an `id`:

```php
use Patchlevel\EventSourcing\Aggregate\Uuid;
Expand Down Expand Up @@ -56,7 +56,7 @@ final class GuestIsCheckedOut
}
```
:::note
You can find out more about events [here](events.md).
You can find out more about [events](events.md).
:::

## Define aggregates
Expand Down Expand Up @@ -148,7 +148,7 @@ final class Hotel extends BasicAggregateRoot
}
```
:::note
You can find out more about aggregates [here](aggregate.md).
You can find out more about [aggregates](aggregate.md).
:::

## Define projections
Expand Down Expand Up @@ -178,7 +178,7 @@ final class HotelProjector
/** @return list<array{id: string, name: string, guests: int}> */
public function getHotels(): array
{
return $this->db->fetchAllAssociative(sprintf('SELECT id, name, guests FROM %s;'), self::TABLE);
return $this->db->fetchAllAssociative(sprintf('SELECT id, name, guests FROM %s;', self::TABLE));
}

#[Subscribe(HotelCreated::class)]
Expand Down Expand Up @@ -226,7 +226,7 @@ final class HotelProjector
}
```
:::note
You can find out more about projector [here](subscription.md).
You can find out more about [projectors](subscription.md).
:::

## Processor
Expand Down Expand Up @@ -257,7 +257,7 @@ final class SendCheckInEmailProcessor
}
```
:::note
You can find out more about processor [here](subscription.md).
You can find out more about [processors](subscription.md).
:::

## Configuration
Expand Down Expand Up @@ -326,15 +326,15 @@ $repositoryManager = new RunSubscriptionEngineRepositoryManager(
$hotelRepository = $repositoryManager->get(Hotel::class);
```
:::note
You can find out more about stores [here](store.md).
You can find out more about [stores](store.md).
:::

:::note
The `RunSubscriptionEngineRepositoryManager` is a decorator that triggers the
Subscription Engine when an Aggregate is saved. Normally, you'd use the
`DefaultRepositoryManager` and a worker to run the Subscription Engine.

Learn more [here](subscription.md).
Learn more about the [subscription engine](subscription.md).
:::

## Database setup
Expand Down Expand Up @@ -369,7 +369,7 @@ $schemaDirector->create();
$engine->setup(skipBooting: true);
```
:::note
you can use the predefined [cli commands](cli.md) for this.
You can use the predefined [cli commands](cli.md) for this.
:::

## Usage
Expand All @@ -392,11 +392,11 @@ $hotel2 = $hotelRepository->load(Uuid::fromString('d0d0d0d0-d0d0-d0d0-d0d0-d0d0d
$hotel2->checkIn('David');
$hotelRepository->save($hotel2);

$hotels = $hotelProjection->getHotels();
$hotels = $hotelProjector->getHotels();
```
:::note
You can also use other forms of IDs such as uuid version 6 or a custom format.
You can find more about this [here](aggregate-id.md).
You can find more about this in the [aggregate id](aggregate-id.md) documentation.
:::

## Result
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ powered by the reliable Doctrine ecosystem and focused on developer experience.
* Versioned and managed lifecycle of [subscriptions](subscription.md) like projections and processors
* Safe usage of [Personal Data](personal-data.md) with crypto-shredding
* Smooth [upcasting](upcasting.md) of old events
* Simple setup with [scheme management](store.md) and [doctrine migration](store.md)
* Simple setup with [schema management](store.md) and [doctrine migration](store.md)
* Built in [cli commands](cli.md) with [symfony](https://symfony.com/)
* and much more...

Expand Down
Loading
Loading