diff --git a/.github/workflows/docs-build-try.yml b/.github/workflows/docs-build-try.yml
deleted file mode 100644
index bd943e1c8..000000000
--- a/.github/workflows/docs-build-try.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-name: Try build docs
-
-on:
- pull_request:
- push:
- branches:
- - "renovate/*"
-
-jobs:
- build-try:
- name: Deploy docs
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
- with:
- fetch-depth: 0
-
- - name: Set up Python
- uses: actions/setup-python@v6
- with:
- python-version: "3.x"
-
- - name: Install dependencies
- run: pip install -r docs/requirements.txt
-
- - name: Build docs
- working-directory: docs/
- run: |
- mkdocs build --strict
\ No newline at end of file
diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml
deleted file mode 100644
index e84f21482..000000000
--- a/.github/workflows/docs-build.yml
+++ /dev/null
@@ -1,93 +0,0 @@
-name: Publish docs
-
-on:
- push:
- branches:
- - "[0-9]+.[0-9]+.x"
- release:
- types:
- - published
-
-jobs:
- build:
- name: Deploy docs
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
- with:
- fetch-depth: 0
-
- - name: Set up Python
- uses: actions/setup-python@v6
- with:
- python-version: "3.x"
-
- - name: Install dependencies
- run: pip install -r docs/requirements.txt
-
- - name: Set up git author
- uses: oleksiyrudenko/gha-git-credentials@v2
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Find latest release
- id: latest_release
- uses: pozetroninc/github-action-get-latest-release@v0.8.0
- with:
- repository: ${{ github.repository }}
- excludes: draft,prerelease
-
- - name: Normalize current versions
- id: current
- uses: actions/github-script@v8
- with:
- script: return context.ref.match(/([0-9]+\.[0-9]+)\.(x|[0-9]+)/i)[1];
- result-encoding: string
-
- - name: Normalize latest versions
- id: latest
- uses: actions/github-script@v8
- with:
- script: return "${{steps.latest_release.outputs.release}}".match(/([0-9]+\.[0-9]+)\.[0-9]+/i)[1];
- result-encoding: string
-
- - name: Setup deploy key
- env:
- REPOSITORY_ACCESS_KEY: ${{ secrets.REPOSITORY_ACCESS_KEY }}
- run: |
- # Setup SSH deploy key
- mkdir -p ~/.ssh
- echo "${REPOSITORY_ACCESS_KEY}" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H github.com > ~/.ssh/known_hosts
-
- - run: git remote add doc git@github.com:patchlevel/event-sourcing-docs.git
- - run: git fetch doc gh-pages --verbose
-
- - run: |
- current_major=$(echo "${{ steps.current.outputs.result }}" | cut -d '.' -f 1)
- current_minor=$(echo "${{ steps.current.outputs.result }}" | cut -d '.' -f 2)
-
- latest_major=$(echo "${{ steps.latest.outputs.result }}" | cut -d '.' -f 1)
- latest_minor=$(echo "${{ steps.latest.outputs.result }}" | cut -d '.' -f 2)
-
- if [ "${{ steps.current.outputs.result }}" = "${{ steps.latest.outputs.result }}" ]
- then
- # Here we deploy a new latest version
- mike deploy latest --config-file docs/mkdocs.yml --title="${{ steps.current.outputs.result }} (latest)" --push --remote doc
- elif [ "$current_major" -lt "$latest_major" ] || \
- { [ "$current_major" -eq "$latest_major" ] && [ "$current_minor" -lt "$latest_minor" ]; }
- then
- # Here we deploy a version that's not the latest one and smaller as the latest version
- mike deploy ${{ steps.current.outputs.result }} --config-file docs/mkdocs.yml --push --remote doc
- fi
- - run: |
- # Check if the "latest" alias exists
- HAS_LATEST=$(mike list --config-file docs/mkdocs.yml --rebase --remote doc | grep latest) || true
-
- # If so then it is set as the default version (to enable the index redirect)
- if [ "${HAS_LATEST}" != "" ]
- then
- echo "Set latest as default"
- mike set-default latest --config-file docs/mkdocs.yml --remote doc
- fi
\ No newline at end of file
diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml
deleted file mode 100644
index e6b5ca295..000000000
--- a/.github/workflows/docs-check.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
-
-name: "Check Docs"
-
-on:
- pull_request:
- push:
- branches:
- - "[0-9]+.[0-9]+.x"
-
-jobs:
- checkdocs:
- name: "Check Docs"
-
- runs-on: ${{ matrix.operating-system }}
-
- strategy:
- matrix:
- dependencies:
- - "locked"
- php-version:
- - "8.5"
- operating-system:
- - "ubuntu-latest"
-
- steps:
- - name: "Checkout"
- uses: actions/checkout@v6
-
- - name: "Install PHP"
- uses: "shivammathur/setup-php@2.36.0"
- with:
- coverage: none
- php-version: "${{ matrix.php-version }}"
- ini-values: memory_limit=-1, opcache.enable_cli=1
-
- - uses: ramsey/composer-install@3.1.1
- with:
- dependency-versions: ${{ matrix.dependencies }}
-
- - name: "extract php code"
- run: "bin/docs-extract-php-code"
-
- - name: "lint php"
- run: "php -l docs_php/*.php"
-
- - name: "docs code style"
- run: "vendor/bin/phpcbf docs_php --exclude=SlevomatCodingStandard.TypeHints.DeclareStrictTypes"
\ No newline at end of file
diff --git a/docs/pages/UPGRADE-4.0.md b/docs/UPGRADE-4.0.md
similarity index 100%
rename from docs/pages/UPGRADE-4.0.md
rename to docs/UPGRADE-4.0.md
diff --git a/docs/pages/aggregate.md b/docs/aggregate.md
similarity index 91%
rename from docs/pages/aggregate.md
rename to docs/aggregate.md
index 6b00e1e8a..6a9cb8305 100644
--- a/docs/pages/aggregate.md
+++ b/docs/aggregate.md
@@ -4,11 +4,11 @@ The linchpin of event-sourcing is the aggregate. These aggregates can be imagine
One main difference is that we don't save the current state, but only the individual events that led to the state.
This means it is always possible to build the current state again from the events.
-!!! 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).
-
+:::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).
+:::
+
An aggregate must fulfill a few points so that we can use it in event-sourcing:
* It must implement the `AggregateRoot` interface.
@@ -44,14 +44,14 @@ final class Profile extends BasicAggregateRoot
}
}
```
-!!! warning
-
- The aggregate is not yet finished and has only been built to the point that you can instantiate the object.
-
-!!! tip
-
- Find out more about aggregate IDs [here](./identifier.md).
-
+:::warning
+The aggregate is not yet finished and has only been built to the point that you can instantiate the object.
+:::
+
+:::tip
+Find out more about aggregate IDs [here](./identifier.md).
+:::
+
We use a so-called named constructor here to create an object of the AggregateRoot.
The constructor itself is protected and cannot be called from outside.
But it is possible to define different named constructors for different use-cases like `import`.
@@ -76,17 +76,17 @@ final class CreateProfileHandler
}
}
```
-!!! warning
+:::warning
+If you look in the database now, you would see that nothing has been saved.
+This is because only events are stored in the database and as long as no events exist,
+nothing happens.
+:::
- If you look in the database now, you would see that nothing has been saved.
- This is because only events are stored in the database and as long as no events exist,
- nothing happens.
-
-!!! tip
+:::tip
+A **command bus** system is not necessary, only recommended.
+The interaction can also easily take place in a controller or service.
+:::
- A **command bus** system is not necessary, only recommended.
- The interaction can also easily take place in a controller or service.
-
## Create a new aggregate
In order that an aggregate is actually saved, at least one event must exist in the DB.
@@ -107,10 +107,11 @@ final class ProfileRegistered
}
}
```
-!!! note
- You can find out more about events [here](./events.md).
-
+:::note
+You can find out more about events [here](./events.md).
+:::
+
After we have defined the event, we have to adapt the profile aggregate:
```php
@@ -148,9 +149,9 @@ final class Profile extends BasicAggregateRoot
}
}
```
-!!! tip
-
- Prefixing the apply methods with "apply" improves readability.
+:::tip
+Prefixing the apply methods with "apply" improves readability.
+:::
In our named constructor `register` we have now created the event and recorded it with the method `recordThat`.
The aggregate remembers all new recorded events in order to save them later.
@@ -160,9 +161,9 @@ So that the AggregateRoot also knows which method it should call,
we have to mark it with the `Apply` attribute. We did that in the `applyProfileRegistered` method.
In there we then change the state of the aggregate by filling the properties with the values from the event.
-!!! success
-
- The aggregate is now ready to be saved!
+:::success
+The aggregate is now ready to be saved!
+:::
### Modify an aggregate
@@ -181,9 +182,10 @@ final class NameChanged
}
}
```
-!!! note
- Events should best be written in the past, as they describe a state that has happened.
+:::note
+Events should best be written in the past, as they describe a state that has happened.
+:::
After we have defined the event, we can define a new public method called `changeName` to change the profile name.
This method then creates the event `NameChanged` and records it:
@@ -257,13 +259,14 @@ final class ChangeNameHandler
}
}
```
-!!! success
-
- Our aggregate can now be changed and saved.
-
-!!! note
- You can read more about Repository [here](./repository.md).
+:::success
+Our aggregate can now be changed and saved.
+:::
+
+:::note
+You can read more about Repository [here](./repository.md).
+:::
Here the aggregate is loaded from the `repository` by fetching all events from the database.
These events are then executed again with the `apply` methods in order to rebuild the current state.
@@ -354,9 +357,9 @@ final class Profile extends BasicAggregateRoot
}
}
```
-!!! warning
-
- When all events are suppressed, debugging becomes more difficult if you forget an apply method.
+:::warning
+When all events are suppressed, debugging becomes more difficult if you forget an apply method.
+:::
## Stream Name
@@ -393,9 +396,10 @@ final class GuestList extends BasicAggregateRoot
// ...
}
```
-!!! tip
- You can find more about splitting aggregates [here](./aggregate.md#splitting-aggregates).
+:::tip
+You can find more about splitting aggregates [here](./aggregate.md#splitting-aggregates).
+:::
## Business rules
@@ -435,11 +439,12 @@ final class Profile extends BasicAggregateRoot
}
}
```
-!!! danger
- Validations during "apply" should not happen, they will break the rebuilding of the aggregate!
- Instead validate the data *before* the event will be recorded.
-
+:::danger
+Validations during "apply" should not happen, they will break the rebuilding of the aggregate!
+Instead validate the data *before* the event will be recorded.
+:::
+
We have now ensured that this rule takes effect when a name is changed with the method `changeName`.
But when we create a new profile this rule does not currently apply.
@@ -521,14 +526,15 @@ final class NameChanged
}
}
```
-!!! warning
- You need to create a normalizer for the `Name` value object.
- So the payload must be serializable and unserializable as json.
+:::warning
+You need to create a normalizer for the `Name` value object.
+So the payload must be serializable and unserializable as json.
+:::
-!!! note
-
- You can find out more about normalizer [here](./normalizer.md).
+:::note
+You can find out more about normalizer [here](./normalizer.md).
+:::
There are also cases where business rules have to be defined depending on the aggregate state.
Sometimes also from states, which were changed in the same method.
@@ -641,9 +647,9 @@ final class Profile extends BasicAggregateRoot
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).
+:::note
+You can find out more about clock [here](./clock.md).
+:::
## Splitting Aggregates
@@ -760,4 +766,4 @@ $aggregateRegistry = (new AttributeAggregateRootRegistryFactory())->create([/* p
* [How to store and load aggregates](repository.md)
* [How to snapshot aggregates](snapshots.md)
* [How to create Projections](subscription.md)
-* [How to split streams](split_stream.md)
+* [How to split streams](split-stream.md)
diff --git a/docs/pages/our-backward-compatibility-promise.md b/docs/backward-compatibility.md
similarity index 94%
rename from docs/pages/our-backward-compatibility-promise.md
rename to docs/backward-compatibility.md
index 70f7f1ffd..35f90d3c4 100644
--- a/docs/pages/our-backward-compatibility-promise.md
+++ b/docs/backward-compatibility.md
@@ -40,8 +40,7 @@ compatibility promise.
In our docs the features are marked like this:
-??? example "Experimental"
-
- This feature is still experimental and may change in the future.
- Use it with caution.
-
\ No newline at end of file
+:::experimental
+This feature is still experimental and may change in the future.
+Use it with caution.
+:::
\ No newline at end of file
diff --git a/docs/pages/cli.md b/docs/cli.md
similarity index 93%
rename from docs/pages/cli.md
rename to docs/cli.md
index 0c272a6bf..c2f86d906 100644
--- a/docs/pages/cli.md
+++ b/docs/cli.md
@@ -23,9 +23,9 @@ The database schema can also be created, updated and dropped.
* SchemaUpdateCommand: `event-sourcing:schema:update`
* SchemaDropCommand: `event-sourcing:schema:drop`
-!!! note
-
- You can also register doctrine migration commands.
+:::note
+You can also register doctrine migration commands.
+:::
## Subscription commands
@@ -40,9 +40,9 @@ To manage your subscriptions there are the following cli commands.
* SubscriptionStatusCommand: `event-sourcing:subscription:status`
* SubscriptionTeardownCommand: `event-sourcing:subscription:teardown`
-!!! note
-
- You can find out more about subscriptions [here](subscription.md).
+:::note
+You can find out more about subscriptions [here](subscription.md).
+:::
## Inspector commands
@@ -141,11 +141,11 @@ $cli->addCommands([
new Command\VersionCommand($dependencyFactory, 'event-sourcing:migrations:version'),
]);
```
-!!! note
+:::note
+Here you can find more information on how to
+[configure doctrine migration](https://www.doctrine-project.org/projects/doctrine-migrations/en/3.3/reference/custom-configuration.html).
+:::
- Here you can find more information on how to
- [configure doctrine migration](https://www.doctrine-project.org/projects/doctrine-migrations/en/3.3/reference/custom-configuration.html).
-
## Learn more
* [How to configure store](store.md)
diff --git a/docs/pages/clock.md b/docs/clock.md
similarity index 93%
rename from docs/pages/clock.md
rename to docs/clock.md
index 4939c3e67..6a66fbe9b 100644
--- a/docs/pages/clock.md
+++ b/docs/clock.md
@@ -61,9 +61,10 @@ $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.
+:::note
+The instance of the frozen datetime will be cloned internally, so the it's not the same instance but equals.
+:::
## Learn more
diff --git a/docs/pages/command_bus.md b/docs/command-bus.md
similarity index 88%
rename from docs/pages/command_bus.md
rename to docs/command-bus.md
index 33c2273f1..3b07f42d6 100644
--- a/docs/pages/command_bus.md
+++ b/docs/command-bus.md
@@ -38,25 +38,25 @@ final class CreateProfileHandler
}
}
```
-!!! note
-
- To use Service Handler you need to register the handler in the `ServiceHandlerProvider`.
+:::note
+To use Service Handler you need to register the handler in the `ServiceHandlerProvider`.
+:::
-!!! tip
-
- A class can have multiple handle methods.
+:::tip
+A class can have multiple handle methods.
+:::
### Aggregate Handler
Another way to handle commands is to use the aggregates themselves.
To do this, you need to mark the method that handles the command with the `#[Handle]` attribute.
-!!! note
+:::note
+The aggregates themselves are of course not a service.
+The AggregateHandlerProvider uses the aggregates to create the handlers for you.
+You can find out more about this in the [providers](./command-bus.md#provider) section.
+:::
- The aggregates themselves are of course not a service.
- The AggregateHandlerProvider uses the aggregates to create the handlers for you.
- You can find out more about this in the [providers](./command_bus.md#provider) section.
-
#### Create Aggregate
If you want to create a new aggregate, you need to create a static method that returns a new instance of the aggregate.
@@ -86,9 +86,10 @@ final class Profile extends BasicAggregateRoot
// ... apply methods
}
```
-!!! tip
- You can find more information about aggregates [here](aggregate.md).
+:::tip
+You can find more information about aggregates [here](aggregate.md).
+:::
#### Update Aggregate
@@ -172,13 +173,14 @@ final class Profile extends BasicAggregateRoot
// ... apply methods
}
```
-!!! note
- The service must be registered in the service locator.
+:::note
+The service must be registered in the service locator.
+:::
-!!! tip
-
- You can inject multiple services into the handler method.
+:::tip
+You can inject multiple services into the handler method.
+:::
Or you can inject the service manually using the `#[Inject]` attribute.
There you can specify the service name that should be injected.
@@ -212,9 +214,10 @@ final class Profile extends BasicAggregateRoot
// ... apply methods
}
```
-!!! note
- Injection in handler methods is only possible with the `AggregateHandlerProvider`.
+:::note
+Injection in handler methods is only possible with the `AggregateHandlerProvider`.
+:::
## Setup
@@ -264,19 +267,20 @@ final class CreateProfile
}
}
```
-!!! tip
- You can override the default values for the maximum number of retries and the conditions
- by passing them to the `InstantRetry` attribute.
-
- ```php
- use Patchlevel\EventSourcing\Attribute\InstantRetry;
-
- #[InstantRetry(3, [AggregateOutdated::class])]
- final class CreateProfile
- {
- }
- ```
+:::tip
+You can override the default values for the maximum number of retries and the conditions
+by passing them to the `InstantRetry` attribute.
+
+```php
+use Patchlevel\EventSourcing\Attribute\InstantRetry;
+
+#[InstantRetry(3, [AggregateOutdated::class])]
+final class CreateProfile
+{
+}
+```
+:::
## Provider
@@ -341,9 +345,9 @@ $provider = new AggregateHandlerProvider(
]), // or other psr-11 compatible container
);
```
-!!! tip
-
- You can find suitable implementations of psr-11 containers on [packagist](https://packagist.org/search/?tags=PSR-11).
+:::tip
+You can find suitable implementations of psr-11 containers on [packagist](https://packagist.org/search/?tags=PSR-11).
+:::
### Chain Handler Provider
@@ -363,4 +367,4 @@ $provider = new ChainHandlerProvider([
* [How to use events](events.md)
* [How to use clock](clock.md)
* [How to use aggregate id](identifier.md)
-* [How to use query bus](query_bus.md)
+* [How to use query bus](query-bus.md)
diff --git a/docs/pages/dynamic_consistency_boundary.md b/docs/dynamic-consistency-boundary.md
similarity index 92%
rename from docs/pages/dynamic_consistency_boundary.md
rename to docs/dynamic-consistency-boundary.md
index d580be7d8..e99bf9902 100644
--- a/docs/pages/dynamic_consistency_boundary.md
+++ b/docs/dynamic-consistency-boundary.md
@@ -1,9 +1,9 @@
# Dynamic Consistency Boundary
-??? example "Experimental"
-
- This feature is still experimental and may change in the future.
- Use it with caution.
+:::experimental
+This feature is still experimental and may change in the future.
+Use it with caution.
+:::
Dynamic Consistency Boundary (DCB) is an event‑sourcing approach for making consistent,
cross‑stream decisions without loading full aggregates.
@@ -14,12 +14,12 @@ if the queried subset changes concurrently, the write is rejected and can be ret
This makes handlers simple, fast, and scalable, since only relevant events are processed.
DCB is a great fit when business rules span multiple streams.
-!!! note
-
- You can read more about Dynamic Consistency Boundary on page [dcb.events](https://dcb.events/).
+:::note
+You can read more about Dynamic Consistency Boundary on page [dcb.events](https://dcb.events/).
+:::
Since this approach differs slightly from the standard "aggregate" event sourcing principle,
-we will use the [Getting Started](./getting_started.md) example and build it as a DCB variant.
+we will use the [Getting Started](./getting-started.md) example and build it as a DCB variant.
In our little getting started example, we manage hotels.
We keep the example small, so we can only create hotels and let guests check in and check out.
@@ -85,13 +85,14 @@ final class GuestIsCheckedOut
}
}
```
-!!! note
- You can find out more about events [here](events.md).
-
+:::note
+You can find out more about events [here](events.md).
+:::
+
## Define Commands
-Unlike in the [Getting Started](./getting_started.md) section, we're working with the [Command Bus](./command_bus.md) here.
+Unlike in the [Getting Started](./getting-started.md) section, we're working with the [Command Bus](./command-bus.md) here.
This allows us to express our interaction with the system using commands. We can do the following with our system:
The following command creates a new hotel. It carries the hotel ID and name.
@@ -297,14 +298,15 @@ final class CreateHotelHandler
}
}
```
-!!! note
- Handlers build a Decision Model from the projections and then append events with an optimistic AppendCondition.
- If any relevant event arrives between read and write, the append fails and you can retry.
+:::note
+Handlers build a Decision Model from the projections and then append events with an optimistic AppendCondition.
+If any relevant event arrives between read and write, the append fails and you can retry.
+:::
-!!! tip
-
- To get type security, you can use our [phpstan extension](https://github.com/patchlevel/event-sourcing-phpstan-extension).
+:::tip
+To get type security, you can use our [phpstan extension](https://github.com/patchlevel/event-sourcing-phpstan-extension).
+:::
The next handler implements the `CheckIn` command.
@@ -450,10 +452,10 @@ $commandBus->dispatch(new CheckOut($hotelId, 'David'));
We've seen how to use DCB to make decisions consistently.
In this example we skipped the subscription part,
as it is the same as before with aggregates.
-You can find this [Getting Started](./getting_started.md) section.
+You can find this [Getting Started](./getting-started.md) section.
## Learn more
* [Events](./events.md)
-* [Command Bus](./command_bus.md)
+* [Command Bus](./command-bus.md)
* [Store](./store.md)
diff --git a/docs/pages/event_bus.md b/docs/event-bus.md
similarity index 82%
rename from docs/pages/event_bus.md
rename to docs/event-bus.md
index 9294561c7..c46903d16 100644
--- a/docs/pages/event_bus.md
+++ b/docs/event-bus.md
@@ -6,10 +6,10 @@ For all events that are persisted (when the `save` method has been executed on t
the event wrapped in a message will be dispatched to the `event bus`.
All listeners are then called for each message.
-!!! tip
-
- It is recommended to use the [subscription engine](subscription.md) to process the messages.
- It is more powerful and flexible than the event bus.
+:::tip
+It is recommended to use the [subscription engine](subscription.md) to process the messages.
+It is more powerful and flexible than the event bus.
+:::
## Event Bus
@@ -20,9 +20,10 @@ use Patchlevel\EventSourcing\EventBus\DefaultEventBus;
$eventBus = DefaultEventBus::create([$mailListener]);
```
-!!! note
- The order in which the listeners are executed is determined by the order in which they are passed to the factory.
+:::note
+The order in which the listeners are executed is determined by the order in which they are passed to the factory.
+:::
Internally, the event bus uses the `Consumer` to consume the messages and call the listeners.
@@ -55,9 +56,10 @@ $eventBus = new DefaultEventBus(
new DefaultConsumer($listenerProvider),
);
```
-!!! tip
- The `DefaultEventBus::create` method uses the `DefaultConsumer` and `AttributeListenerProvider` by default.
+:::tip
+The `DefaultEventBus::create` method uses the `DefaultConsumer` and `AttributeListenerProvider` by default.
+:::
### Custom listener provider
@@ -78,10 +80,11 @@ $listenerProvider = new class implements ListenerProvider {
}
};
```
-!!! tip
- You can use `$listenerDiscriptor->name()` to get the name of the listener.
-
+:::tip
+You can use `$listenerDiscriptor->name()` to get the name of the listener.
+:::
+
## Listener
You can listen for specific events with the attribute `Subscribe`.
@@ -100,9 +103,10 @@ final class WelcomeSubscriber
}
}
```
-!!! tip
- If you use psalm, you can use the [event sourcing plugin](https://github.com/patchlevel/event-sourcing-psalm-plugin) for better type support.
+:::tip
+If you use psalm, you can use the [event sourcing plugin](https://github.com/patchlevel/event-sourcing-psalm-plugin) for better type support.
+:::
### Listen on all events
@@ -132,9 +136,10 @@ use Patchlevel\EventSourcing\EventBus\Psr14EventBus;
$eventBus = new Psr14EventBus($psr14EventDispatcher);
```
-!!! warning
- You can't use the `Subscribe` attribute with the psr-14 event bus.
+:::warning
+You can't use the `Subscribe` attribute with the psr-14 event bus.
+:::
## Learn more
diff --git a/docs/pages/events.md b/docs/events.md
similarity index 79%
rename from docs/pages/events.md
rename to docs/events.md
index c2af1761a..29d1ef098 100644
--- a/docs/pages/events.md
+++ b/docs/events.md
@@ -27,21 +27,21 @@ final class ProfileCreated
}
}
```
-!!! warning
-
- The payload must be serializable and unserializable as json.
+:::warning
+The payload must be serializable and unserializable as json.
+:::
-!!! tip
+:::tip
+An event should be named in the past because it has already happened.
+
+Best practice is to prefix the event names with the aggregate name, lowercase everything, and replace spaces with underscores.
+Here are some examples:
+
+* `profile.created`
+* `profile.name_changed`
+* `hotel.guest_checked_out`
+:::
- An event should be named in the past because it has already happened.
-
- Best practice is to prefix the event names with the aggregate name, lowercase everything, and replace spaces with underscores.
- Here are some examples:
-
- * `profile.created`
- * `profile.name_changed`
- * `hotel.guest_checked_out`
-
## Alias
You also have the option to set aliases for the events.
@@ -57,15 +57,15 @@ final class ProfileRegistered
```
When saving, the name will always be used. However, when loading, aliases will also be taken into account.
-!!! note
-
- In the database, the name of the event is always stored,
- allowing the class to be renamed without encountering any issues.
-
-!!! tip
+:::note
+In the database, the name of the event is always stored,
+allowing the class to be renamed without encountering any issues.
+:::
- If you want to make significant changes to an event,
- you can take a look at the [Upcaster](upcasting.md).
+:::tip
+If you want to make significant changes to an event,
+you can take a look at the [Upcaster](upcasting.md).
+:::
## Serializer
@@ -108,14 +108,15 @@ final class ProfileCreated
}
}
```
-!!! tip
- Built-in normalizers like `IdNormalizer` and `DateTimeImmutableNormalizer` can be inferred from the type hint
- and so you don't have to specify them. If you want to configure the Normalizer, you still have to do it.
+:::tip
+Built-in normalizers like `IdNormalizer` and `DateTimeImmutableNormalizer` can be inferred from the type hint
+and so you don't have to specify them. If you want to configure the Normalizer, you still have to do it.
+:::
-!!! note
-
- You can find out more about normalizer [here](normalizer.md).
+:::note
+You can find out more about normalizer [here](normalizer.md).
+:::
## Event Registry
diff --git a/docs/pages/getting_started.md b/docs/getting-started.md
similarity index 91%
rename from docs/pages/getting_started.md
rename to docs/getting-started.md
index 3df5c7ad2..844b9e345 100644
--- a/docs/pages/getting_started.md
+++ b/docs/getting-started.md
@@ -55,9 +55,9 @@ final class GuestIsCheckedOut
}
}
```
-!!! note
-
- You can find out more about events [here](events.md).
+:::note
+You can find out more about events [here](events.md).
+:::
## Define aggregates
@@ -147,9 +147,9 @@ final class Hotel extends BasicAggregateRoot
}
}
```
-!!! note
-
- You can find out more about aggregates [here](aggregate.md).
+:::note
+You can find out more about aggregates [here](aggregate.md).
+:::
## Define projections
@@ -230,10 +230,10 @@ final class HotelProjector
}
}
```
-!!! note
+:::note
+You can find out more about projector [here](subscription.md).
+:::
- You can find out more about projector [here](subscription.md).
-
## Processor
In our example we also want to email the head office as soon as a guest is checked in.
@@ -261,17 +261,17 @@ final class SendCheckInEmailProcessor
}
}
```
-!!! note
+:::note
+You can find out more about processor [here](subscription.md).
+:::
- You can find out more about processor [here](subscription.md).
-
## Configuration
After we have defined everything, we still have to plug the whole thing together:
-!!! tip
-
- If you use symfony, you can use our [symfony bundle](https://event-sourcing-bundle.patchlevel.io/latest/installation/) to skip this step.
+:::tip
+If you use symfony, you can use our [symfony bundle](https://event-sourcing-bundle.patchlevel.io/latest/installation/) to skip this step.
+:::
```php
use Doctrine\DBAL\DriverManager;
@@ -329,9 +329,10 @@ $repositoryManager = new RunSubscriptionEngineRepositoryManager(
$hotelRepository = $repositoryManager->get(Hotel::class);
```
-!!! note
- You can find out more about stores [here](store.md).
+:::note
+You can find out more about stores [here](store.md).
+:::
## Database setup
@@ -364,9 +365,10 @@ $schemaDirector->create();
/** @var SubscriptionEngine $engine */
$engine->setup(skipBooting: true);
```
-!!! note
- you can use the predefined [cli commands](cli.md) for this.
+:::note
+you can use the predefined [cli commands](cli.md) for this.
+:::
## Usage
@@ -390,19 +392,20 @@ $hotelRepository->save($hotel2);
$hotels = $hotelProjection->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](identifier.md).
+:::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](identifier.md).
+:::
## Result
-!!! success
-
- We have successfully implemented and used event sourcing.
+:::success
+We have successfully implemented and used event sourcing.
- Feel free to browse further in the documentation for more detailed information.
- If there are still open questions, create a ticket on Github and we will try to help you.
+Feel free to browse further in the documentation for more detailed information.
+If there are still open questions, create a ticket on Github and we will try to help you.
+:::
## Learn more
diff --git a/docs/pages/identifier.md b/docs/identifier.md
similarity index 96%
rename from docs/pages/identifier.md
rename to docs/identifier.md
index cc101b2d7..7043da70d 100644
--- a/docs/pages/identifier.md
+++ b/docs/identifier.md
@@ -30,9 +30,10 @@ use Patchlevel\EventSourcing\Identifier\Uuid;
$uuid = Uuid::generate();
$uuid = Uuid::fromString('d6e8d7a0-4b0b-4e6a-8a9a-3a0b2d9d0e4e');
```
-!!! note
- UUID v7 provides k‑sortable identifiers that work well with append‑only streams and database indexes. See the ramsey docs for details.
+:::note
+UUID v7 provides k‑sortable identifiers that work well with append‑only streams and database indexes. See the ramsey docs for details.
+:::
### `CustomId`
diff --git a/docs/pages/index.md b/docs/index.md
similarity index 81%
rename from docs/pages/index.md
rename to docs/index.md
index 99a5e8bd5..11a4c9918 100644
--- a/docs/pages/index.md
+++ b/docs/index.md
@@ -9,9 +9,9 @@ powered by the reliable Doctrine ecosystem and focused on developer experience.
* Based on [doctrine dbal](https://github.com/doctrine/dbal) and their ecosystem
* Developer experience oriented and fully typed
* Automatic [snapshot](snapshots.md)-system to boost your performance
-* [Split](split_stream.md) big aggregates into multiple streams
+* [Split](split-stream.md) big aggregates into multiple streams
* Versioned and managed lifecycle of [subscriptions](subscription.md) like projections and processors
-* Safe usage of [Personal Data](personal_data.md) with crypto-shredding
+* 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)
* Built in [cli commands](cli.md) with [symfony](https://symfony.com/)
@@ -27,7 +27,6 @@ composer require patchlevel/event-sourcing
* [Symfony](https://github.com/patchlevel/event-sourcing-bundle)
* [Psalm](https://github.com/patchlevel/event-sourcing-psalm-plugin)
-!!! tip
-
- Start with the [quickstart](./getting_started.md) to get a feeling for the library.
-
\ No newline at end of file
+:::tip
+Start with the [quickstart](./getting-started.md) to get a feeling for the library.
+:::
\ No newline at end of file
diff --git a/docs/pages/message_decorator.md b/docs/message-decorator.md
similarity index 90%
rename from docs/pages/message_decorator.md
rename to docs/message-decorator.md
index cff647a56..3cf138742 100644
--- a/docs/pages/message_decorator.md
+++ b/docs/message-decorator.md
@@ -10,7 +10,7 @@ We offer a few decorators that you can use.
### SplitStreamDecorator
-In order to use the [split stream](split_stream.md) feature, the `SplitStreamDecorator` must be added.
+In order to use the [split stream](split-stream.md) feature, the `SplitStreamDecorator` must be added.
```php
use Patchlevel\EventSourcing\Metadata\Event\AttributeEventMetadataFactory;
@@ -66,9 +66,9 @@ $repositoryManager = new DefaultRepositoryManager(
$repository = $repositoryManager->get(Profile::class);
```
-!!! note
-
- You can find out more about repository [here](repository.md).
+:::note
+You can find out more about repository [here](repository.md).
+:::
## Create own decorator
@@ -97,13 +97,13 @@ final class OnSystemRecordedDecorator implements MessageDecorator
}
}
```
-!!! note
-
- The Message is immutable, for more information look up [here](message.md).
+:::note
+The Message is immutable, for more information look up [here](message.md).
+:::
-!!! tip
-
- You can also set multiple headers with `withHeaders` which expects an hashmap.
+:::tip
+You can also set multiple headers with `withHeaders` which expects an hashmap.
+:::
## Learn more
diff --git a/docs/pages/message.md b/docs/message.md
similarity index 89%
rename from docs/pages/message.md
rename to docs/message.md
index e0c3921ed..a50248c07 100644
--- a/docs/pages/message.md
+++ b/docs/message.md
@@ -11,10 +11,10 @@ use Patchlevel\EventSourcing\Message\Message;
$message = Message::create(new NameChanged('foo'));
```
-!!! note
-
- You don't have to create the message yourself, it is automatically created, saved and dispatched in
- the [repository](repository.md).
+:::note
+You don't have to create the message yourself, it is automatically created, saved and dispatched in
+the [repository](repository.md).
+:::
You can add a header using `withHeader`:
@@ -32,9 +32,9 @@ $message = Message::create(new NameChanged('foo'))
recordedOn: $clock->now(),
));
```
-!!! note
-
- The message object is immutable. It creates a new instance with the new data.
+:::note
+The message object is immutable. It creates a new instance with the new data.
+:::
You can also access the headers:
@@ -80,14 +80,14 @@ use Patchlevel\EventSourcing\Message\Message;
$message = Message::create(new NameChanged('foo'))
->withHeader(new ApplicationHeader('app'));
```
-!!! warning
-
- The header needs to be serializable. The library uses the hydrator to serialize and deserialize the headers.
- So you can add normalize attributes to the properties if needed.
+:::warning
+The header needs to be serializable. The library uses the hydrator to serialize and deserialize the headers.
+So you can add normalize attributes to the properties if needed.
+:::
-!!! note
-
- You can read about how to pass additional headers to the message object in the [message decorator](message_decorator.md) docs.
+:::note
+You can read about how to pass additional headers to the message object in the [message decorator](message-decorator.md) docs.
+:::
You can also access your custom headers:
@@ -211,14 +211,15 @@ use Patchlevel\EventSourcing\Message\Translator\RecalculatePlayheadTranslator;
$translator = new RecalculatePlayheadTranslator();
```
-!!! warning
- The `RecalculatePlayheadTranslator` is and need to be stateful.
- You can't reuse the translator for multiple streams.
+:::warning
+The `RecalculatePlayheadTranslator` is and need to be stateful.
+You can't reuse the translator for multiple streams.
+:::
-!!! tip
-
- If you migrate your event stream, you can use the `RecalculatePlayheadTranslator` to fix the playhead.
+:::tip
+If you migrate your event stream, you can use the `RecalculatePlayheadTranslator` to fix the playhead.
+:::
### Chain
@@ -277,14 +278,14 @@ final class SplitProfileCreatedTranslator implements Translator
}
}
```
-!!! warning
-
- Since we changed the number of messages, we have to recalculate the playhead.
+:::warning
+Since we changed the number of messages, we have to recalculate the playhead.
+:::
-!!! tip
-
- You don't have to migrate the store directly for every change,
- but you can also use the [upcasting](upcasting.md) feature.
+:::tip
+You don't have to migrate the store directly for every change,
+but you can also use the [upcasting](upcasting.md) feature.
+:::
## Reducer
@@ -375,8 +376,8 @@ $state = (new Reducer())
```
## Learn more
-* [How to decorate messages](message_decorator.md)
+* [How to decorate messages](message-decorator.md)
* [How to load aggregates](repository.md)
* [How to store messages](store.md)
* [How to use subscriptions](subscription.md)
-* [How to use the event bus](event_bus.md)
+* [How to use the event bus](event-bus.md)
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
deleted file mode 100644
index 0cbac3c1f..000000000
--- a/docs/mkdocs.yml
+++ /dev/null
@@ -1,129 +0,0 @@
-# yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json
-
-site_url: https://event-sourcing.patchlevel.io/
-repo_url: https://github.com/patchlevel/event-sourcing
-repo_name: patchlevel/event-sourcing
-edit_uri: edit/2.0.x/docs/pages/
-docs_dir: pages
-site_name: Event Sourcing for PHP
-site_description: An event sourcing library for php, complete with all the essential features, powered by the reliable Doctrine ecosystem and focused on developer experience.
-site_author: patchlevel
-
-extra:
- meta:
- image: img/patchlevel-banner.png
- social:
- - icon: fontawesome/brands/github
- link: https://github.com/patchlevel
- - icon: fontawesome/solid/globe
- link: https://patchlevel.de
- version:
- provider: mike
- canonical_version: latest
- analytics:
- provider: custom
-
-extra_css:
- - stylesheets/extra.css
-
-theme:
- name: material
- custom_dir: overrides
- logo: assets/logo.png
- favicon: /favicon.ico
- features:
- - navigation.sections
- - navigation.top
- - navigation.indexes
- - navigation.footer
- - content.code.annotate
- - content.code.copy
- palette:
- - media: "(prefers-color-scheme: light)"
- scheme: default
- primary: black
- accent: blue
- toggle:
- icon: material/brightness-7
- name: Switch to dark mode
- - media: "(prefers-color-scheme: dark)"
- scheme: slate
- primary: black
- accent: blue
- toggle:
- icon: material/brightness-4
- name: Switch to light mode
-
-plugins:
- - search:
- - mike:
- canonical_version: latest
- - privacy:
-
-markdown_extensions:
- - meta
- - pymdownx.details
- - pymdownx.highlight:
- anchor_linenums: true
- extend_pygments_lang:
- - name: php
- lang: php
- options:
- startinline: true
- - pymdownx.inlinehilite
- - pymdownx.snippets:
- auto_append:
- - docs/includes/links.md
- - admonition
- - pymdownx.emoji:
- emoji_index: !!python/name:material.extensions.emoji.twemoji
- emoji_generator: !!python/name:materialx.emoji.to_svg
- - def_list
- - pymdownx.tasklist:
- custom_checkbox: true
- - pymdownx.superfences:
- custom_fences:
- - name: mermaid
- class: mermaid
- format: !!python/name:pymdownx.superfences.fence_code_format
-
-nav:
- - Introduction: index.md
- - Getting Started: getting_started.md
- - Basics:
- - Aggregate: aggregate.md
- - Events: events.md
- - Repository: repository.md
- - Message: message.md
- - Store: store.md
- - Subscription: subscription.md
- - Command Bus: command_bus.md
- - Event Bus: event_bus.md
- - Query Bus: query_bus.md
- - Advanced:
- - Dynamic Consistency Boundary: dynamic_consistency_boundary.md
- - Identifier: identifier.md
- - Normalizer: normalizer.md
- - Snapshots: snapshots.md
- - Personal Data: personal_data.md
- - Upcasting: upcasting.md
- - Message Decorator: message_decorator.md
- - Split Stream: split_stream.md
- - Time / Clock: clock.md
- - Testing: testing.md
- - CLI: cli.md
- - Supported Versions: supported-versions.md
- - Our BC Promise: our-backward-compatibility-promise.md
- - Upgrade from 3.x: UPGRADE-4.0.md
- - Links:
- - Blog: https://patchlevel.de/blog
- - Symfony Bundle: https://patchlevel.github.io/event-sourcing-bundle-docs/latest/
- - Admin Bundle: https://github.com/patchlevel/event-sourcing-admin-bundle
- - Psalm Plugin: https://github.com/patchlevel/event-sourcing-psalm-plugin
- - Hydrator: https://github.com/patchlevel/hydrator
-
-validation:
- omitted_files: warn
- absolute_links: warn # Or 'relative_to_docs' - new in MkDocs 1.6
- unrecognized_links: warn
- #anchors: warn
\ No newline at end of file
diff --git a/docs/pages/normalizer.md b/docs/normalizer.md
similarity index 81%
rename from docs/pages/normalizer.md
rename to docs/normalizer.md
index c4fa7ed51..4701a3704 100644
--- a/docs/pages/normalizer.md
+++ b/docs/normalizer.md
@@ -4,10 +4,10 @@ Sometimes you also want to add more complex data in events as payload or in aggr
For example DateTime, enums or value objects.
Here you can use the normalizer to define how the data should be saved and loaded.
-!!! note
-
- The underlying system called hydrator exists as a library.
- You can find out more details [here](https://github.com/patchlevel/hydrator).
+:::note
+The underlying system called hydrator exists as a library.
+You can find out more details [here](https://github.com/patchlevel/hydrator).
+:::
## Usage
@@ -28,10 +28,10 @@ Most built-in normalizers can be inferred from the type hint:
* `Enum` => `EnumNormalizer`
* `AggregateRootId` => `IdNormalizer`
-!!! note
-
- `ObjectNormalizer` will not be inferred. You have to specify it yourself.
- This should prevent you from accidentally serializing objects that you don't want to serialize.
+:::note
+`ObjectNormalizer` will not be inferred. You have to specify it yourself.
+This should prevent you from accidentally serializing objects that you don't want to serialize.
+:::
The other way is to specify the normalizer to the properties directly.
This example is equivalent to the previous one.
@@ -75,9 +75,9 @@ final class Item
}
}
```
-!!! note
-
- With the `ObjectNormalizer`, you can seraialize and deserialize recursively.
+:::note
+With the `ObjectNormalizer`, you can seraialize and deserialize recursively.
+:::
### Event
@@ -99,9 +99,10 @@ final class CreateHotel
}
}
```
-!!! note
- If you have personal data, you can use [crypto-shredding](personal_data.md).
+:::note
+If you have personal data, you can use [crypto-shredding](personal-data.md).
+:::
### Aggregate
@@ -125,9 +126,10 @@ final class Hotel extends BasicAggregateRoot
// ...
}
```
-!!! note
- You can learn more about snapshots [here](snapshots.md).
+:::note
+You can learn more about snapshots [here](snapshots.md).
+:::
## Built-in Normalizer
@@ -150,9 +152,10 @@ final class DTO
public array $dates;
}
```
-!!! note
- The keys from the arrays are taken over here.
+:::note
+The keys from the arrays are taken over here.
+:::
### DateTimeImmutable
@@ -168,9 +171,10 @@ final class DTO
public DateTimeImmutable $date;
}
```
-!!! tip
- You can let the hydrator guess the normalizer from the type hint.
+:::tip
+You can let the hydrator guess the normalizer from the type hint.
+:::
You can also define the format. Either describe it yourself as a string or use one of the existing constants.
The default is `DateTimeImmutable::ATOM`.
@@ -184,9 +188,10 @@ final class DTO
public DateTimeImmutable $date;
}
```
-!!! note
- You can read about how the format is structured in the [php docs](https://www.php.net/manual/de/datetime.format.php).
+:::note
+You can read about how the format is structured in the [php docs](https://www.php.net/manual/de/datetime.format.php).
+:::
### DateTime
@@ -201,9 +206,10 @@ final class DTO
public DateTime $date;
}
```
-!!! tip
- You can let the hydrator guess the normalizer from the type hint.
+:::tip
+You can let the hydrator guess the normalizer from the type hint.
+:::
You can also specify the format here. The default is `DateTime::ATOM`.
@@ -216,15 +222,16 @@ final class DTO
public DateTime $date;
}
```
-!!! warning
- It is highly recommended to only ever use DateTimeImmutable objects and the DateTimeImmutableNormalizer.
- This prevents you from accidentally changing the state of the DateTime and thereby causing bugs.
+:::warning
+It is highly recommended to only ever use DateTimeImmutable objects and the DateTimeImmutableNormalizer.
+This prevents you from accidentally changing the state of the DateTime and thereby causing bugs.
+:::
-!!! note
+:::note
+You can read about how the format is structured in the [php docs](https://www.php.net/manual/de/datetime.format.php).
+:::
- You can read about how the format is structured in the [php docs](https://www.php.net/manual/de/datetime.format.php).
-
### DateTimeZone
To normalize a `DateTimeZone` one can use the `DateTimeZoneNormalizer`.
@@ -238,9 +245,10 @@ final class DTO
public DateTimeZone $timeZone;
}
```
-!!! tip
- You can let the hydrator guess the normalizer from the type hint.
+:::tip
+You can let the hydrator guess the normalizer from the type hint.
+:::
### Enum
@@ -255,9 +263,10 @@ final class DTO
public Status $status;
}
```
-!!! tip
- You can let the hydrator guess the normalizer from the type hint.
+:::tip
+You can let the hydrator guess the normalizer from the type hint.
+:::
You can also specify the enum class.
@@ -284,9 +293,10 @@ final class DTO
public Uuid $id;
}
```
-!!! tip
- You can let the hydrator guess the normalizer from the type hint.
+:::tip
+You can let the hydrator guess the normalizer from the type hint.
+:::
Optional you can also define the type of the id.
@@ -383,9 +393,10 @@ class NameNormalizer implements Normalizer
}
}
```
-!!! warning
- The important thing is that the result of Normalize is serializable!
+:::warning
+The important thing is that the result of Normalize is serializable!
+:::
Now we can also use the normalizer directly.
@@ -396,9 +407,10 @@ final class DTO
public Name $name;
}
```
-!!! tip
- Every normalizer, including the custom normalizer, can be used both for the events and for the snapshots.
+:::tip
+Every normalizer, including the custom normalizer, can be used both for the events and for the snapshots.
+:::
Or define it on class level, so you don't have to specify it for each property.
@@ -430,15 +442,16 @@ The whole thing looks like this
"profile_name": "David"
}
```
-!!! tip
- You can also rename properties to events without having a backwards compatibility break by keeping the serialized name.
+:::tip
+You can also rename properties to events without having a backwards compatibility break by keeping the serialized name.
+:::
-!!! note
-
- NormalizedName also works for snapshots.
- But since a snapshot is just a cache, you can also just invalidate it,
- if you have backwards compatibility break in the property name
+:::note
+NormalizedName also works for snapshots.
+But since a snapshot is just a cache, you can also just invalidate it,
+if you have backwards compatibility break in the property name
+:::
## Ignore
@@ -459,4 +472,4 @@ final class DTO
* [How to define aggregates](aggregate.md)
* [How to define events](events.md)
* [How to snapshot aggregates](snapshots.md)
-* [How to work with personal data](personal_data.md)
+* [How to work with personal data](personal-data.md)
diff --git a/docs/overrides/assets/favicon.png b/docs/overrides/assets/favicon.png
deleted file mode 100644
index 86e8fb7be..000000000
Binary files a/docs/overrides/assets/favicon.png and /dev/null differ
diff --git a/docs/overrides/assets/logo.png b/docs/overrides/assets/logo.png
deleted file mode 100644
index be170fedb..000000000
Binary files a/docs/overrides/assets/logo.png and /dev/null differ
diff --git a/docs/overrides/main.html b/docs/overrides/main.html
deleted file mode 100644
index da67f541c..000000000
--- a/docs/overrides/main.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "base.html" %}
-
-{% block extrahead %}
-
-
-
-
-
-
-
-
-
-{% endblock %}
-
-{% block outdated %}
-You're not viewing the latest version.
-
- Click here to go to latest.
-
-{% endblock %}
\ No newline at end of file
diff --git a/docs/overrides/partials/integrations/analytics/custom.html b/docs/overrides/partials/integrations/analytics/custom.html
deleted file mode 100644
index f72401389..000000000
--- a/docs/overrides/partials/integrations/analytics/custom.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/pages/navigation.json b/docs/pages/navigation.json
deleted file mode 100644
index 09f5e3831..000000000
--- a/docs/pages/navigation.json
+++ /dev/null
@@ -1,134 +0,0 @@
-[
- {
- "slug": "event-sourcing",
- "title": "Introduction",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/getting_started",
- "title": "Getting Started",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/basics",
- "title": "Basics",
- "subEntries": [
- {
- "slug": "event-sourcing/aggregate",
- "title": "Aggregate",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/events",
- "title": "Events",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/repository",
- "title": "Repository",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/message",
- "title": "Message",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/store",
- "title": "Store",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/subscription",
- "title": "Subscription",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/command_bus",
- "title": "Command Bus",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/event_bus",
- "title": "Event Bus",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/query_bus",
- "title": "Query Bus",
- "subEntries": null
- }
- ]
- },
- {
- "slug": "event-sourcing/advanced",
- "title": "Advanced",
- "subEntries": [
- {
- "slug": "event-sourcing/aggregate_id",
- "title": "Aggregate ID",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/normalizer",
- "title": "Normalizer",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/snapshots",
- "title": "Snapshots",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/personal_data",
- "title": "Personal Data",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/upcasting",
- "title": "Upcasting",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/message_decorator",
- "title": "Message Decorator",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/split_stream",
- "title": "Split Stream",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/clock",
- "title": "Time / Clock",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/testing",
- "title": "Testing",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/cli",
- "title": "CLI",
- "subEntries": null
- }
- ]
- },
- {
- "slug": "event-sourcing/supported-versions",
- "title": "Supported Versions",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/our-backward-compatibility-promise",
- "title": "Our BC Promise",
- "subEntries": null
- },
- {
- "slug": "event-sourcing/UPGRADE-3.0",
- "title": "Upgrade from 2.x",
- "subEntries": null
- }
-]
diff --git a/docs/pages/stylesheets/extra.css b/docs/pages/stylesheets/extra.css
deleted file mode 100644
index 26494c71e..000000000
--- a/docs/pages/stylesheets/extra.css
+++ /dev/null
@@ -1,43 +0,0 @@
-:root > * {
- --md-code-hl-number-color: #6897BB;
- --md-code-hl-special-color: #C7CDD7;
- --md-code-hl-function-color: #FFC66D;
- --md-code-hl-constant-color: #9876AA;
- --md-code-hl-keyword-color: #CC7832;
- --md-code-hl-string-color: #6A8759;
- --md-code-hl-name-color: red;
- --md-code-hl-operator-color: #C7CDD7;
- --md-code-hl-punctuation-color: #C7CDD7;
- --md-code-hl-comment-color: #629755;
- --md-code-hl-generic-color: red;
- --md-code-hl-variable-color: #9876AA;
-
- --md-code-fg-color: #C7CDD7;
- --md-code-bg-color: rgb(39, 42, 53);
- --md-code-hl-color: red;
-}
-
-p {
- margin-block-start: 2em;
- margin-block-end: 2em;
-}
-
-.md-typeset code {
- padding: .2em .4em;
- font-size: .9em;
- line-height: 1.8;
- border-radius: .2rem;
-}
-
-[data-md-color-scheme=default] .md-typeset code:not(pre code) {
- background-color: #f5f5f5;
- color: #36464e;
-}
-
-.admonition-title {
- margin-bottom: -1em !important;
-}
-
-.md-nav__link--active {
- font-weight: bold;
-}
\ No newline at end of file
diff --git a/docs/pages/personal_data.md b/docs/personal-data.md
similarity index 89%
rename from docs/pages/personal_data.md
rename to docs/personal-data.md
index fdfb9703f..cb95cc3a2 100644
--- a/docs/pages/personal_data.md
+++ b/docs/personal-data.md
@@ -39,10 +39,11 @@ final class EmailChanged
}
}
```
-!!! tip
- You can use the `DataSubjectId` in aggregates for snapshots too.
-
+:::tip
+You can use the `DataSubjectId` in aggregates for snapshots too.
+:::
+
### PersonalData
Next, you have to mark the properties that should be encrypted with the `#[PersonalData]` attribute.
@@ -63,9 +64,10 @@ final class EmailChanged
}
}
```
-!!! tip
- You can use the `PersonalData` in aggregates for snapshots too.
+:::tip
+You can use the `PersonalData` in aggregates for snapshots too.
+:::
If the information could not be decrypted, then a fallback value will be used.
The default fallback value is `null`.
@@ -92,13 +94,14 @@ final class ProfileChanged
}
}
```
-!!! danger
- You have to deal with this case in your business logic such as aggregates and subscriptions.
+:::danger
+You have to deal with this case in your business logic such as aggregates and subscriptions.
+:::
-!!! note
-
- The normalized data is encrypted. This means that this happens after the `extract` or before the `hydrate`.
+:::note
+The normalized data is encrypted. This means that this happens after the `extract` or before the `hydrate`.
+:::
## Setup
@@ -149,9 +152,10 @@ use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;
/** @var CipherKeyStore $cipherKeyStore */
$cryptographer = PersonalDataPayloadCryptographer::createWithDefaultSettings($cipherKeyStore);
```
-!!! tip
- You can specify the cipher method with the second parameter.
+:::tip
+You can specify the cipher method with the second parameter.
+:::
### Event Serializer Integration
@@ -167,9 +171,10 @@ DefaultEventSerializer::createFromPaths(
cryptographer: $cryptographer,
);
```
-!!! note
- More information about the events can be found [here](./events.md).
+:::note
+More information about the events can be found [here](./events.md).
+:::
### Snapshot Store Integration
@@ -187,13 +192,14 @@ $snapshotStore = DefaultSnapshotStore::createDefault(
$cryptographer,
);
```
-!!! note
- More information about the snapshot store can be found [here](./snapshots.md).
+:::note
+More information about the snapshot store can be found [here](./snapshots.md).
+:::
-!!! success
-
- Now you can save and read events with personal data.
+:::success
+Now you can save and read events with personal data.
+:::
## Remove personal data
diff --git a/docs/project.json b/docs/project.json
new file mode 100644
index 000000000..b2b8072b8
--- /dev/null
+++ b/docs/project.json
@@ -0,0 +1,110 @@
+{
+ "navigation": [
+ {
+ "title": "Introduction",
+ "file": "index.md"
+ },
+ {
+ "title": "Getting Started",
+ "file": "getting-started.md"
+ },
+ {
+ "title": "Basics",
+ "subEntries": [
+ {
+ "title": "Aggregate",
+ "file": "aggregate.md"
+ },
+ {
+ "title": "Events",
+ "file": "events.md"
+ },
+ {
+ "title": "Repository",
+ "file": "repository.md"
+ },
+ {
+ "title": "Message",
+ "file": "message.md"
+ },
+ {
+ "title": "Store",
+ "file": "store.md"
+ },
+ {
+ "title": "Subscription",
+ "file": "subscription.md"
+ },
+ {
+ "title": "Command Bus",
+ "file": "command-bus.md"
+ },
+ {
+ "title": "Event Bus",
+ "file": "event-bus.md"
+ },
+ {
+ "title": "Query Bus",
+ "file": "query-bus.md"
+ }
+ ]
+ },
+ {
+ "title": "Advanced",
+ "subEntries": [
+ {
+ "title": "Identifier",
+ "file": "identifier.md"
+ },
+ {
+ "title": "Normalizer",
+ "file": "normalizer.md"
+ },
+ {
+ "title": "Snapshots",
+ "file": "snapshots.md"
+ },
+ {
+ "title": "Personal Data",
+ "file": "personal-data.md"
+ },
+ {
+ "title": "Upcasting",
+ "file": "upcasting.md"
+ },
+ {
+ "title": "Message Decorator",
+ "file": "message-decorator.md"
+ },
+ {
+ "title": "Split Stream",
+ "file": "split-stream.md"
+ },
+ {
+ "title": "Time / Clock",
+ "file": "clock.md"
+ },
+ {
+ "title": "Testing",
+ "file": "testing.md"
+ },
+ {
+ "title": "CLI",
+ "file": "cli.md"
+ }
+ ]
+ },
+ {
+ "title": "Supported Versions",
+ "file": "supported-versions.md"
+ },
+ {
+ "title": "Our BC Promise",
+ "file": "backward-compatibility.md"
+ },
+ {
+ "title": "Upgrade from 3.x",
+ "file": "UPGRADE-4.0.md"
+ }
+ ]
+}
diff --git a/docs/pages/query_bus.md b/docs/query-bus.md
similarity index 86%
rename from docs/pages/query_bus.md
rename to docs/query-bus.md
index fba5c77e0..dbd9df228 100644
--- a/docs/pages/query_bus.md
+++ b/docs/query-bus.md
@@ -35,17 +35,18 @@ final class QueryProfileHandler
}
}
```
-!!! warning
- A query can only be answered by one method.
+:::warning
+A query can only be answered by one method.
+:::
-!!! note
-
- To use Service Handler you need to register the handler in the `ServiceHandlerProvider`.
+:::note
+To use Service Handler you need to register the handler in the `ServiceHandlerProvider`.
+:::
-!!! tip
-
- A class can have multiple methods which answers different queries.
+:::tip
+A class can have multiple methods which answers different queries.
+:::
### Projector
@@ -67,10 +68,11 @@ final class ProfileProjector
// projector related methods to maintain the state of profiles
}
```
-!!! tip
- Using small dedicated projections for each usecase is best practice. Using them directly as query handlers are
- endoresed and can reduce fragmentation of the system.
+:::tip
+Using small dedicated projections for each usecase is best practice. Using them directly as query handlers are
+endoresed and can reduce fragmentation of the system.
+:::
## Setup
@@ -122,4 +124,4 @@ $provider = new ChainHandlerProvider([
* [How to use aggregates](aggregate.md)
* [How to use events](events.md)
* [How to use subscriptions](subscription.md)
-* [How to use command bus](command_bus.md)
+* [How to use command bus](command-bus.md)
diff --git a/docs/pages/repository.md b/docs/repository.md
similarity index 75%
rename from docs/pages/repository.md
rename to docs/repository.md
index d66cb8302..652e79c37 100644
--- a/docs/pages/repository.md
+++ b/docs/repository.md
@@ -31,9 +31,10 @@ $repositoryManager = new DefaultRepositoryManager(
$repository = $repositoryManager->get(Profile::class);
```
-!!! note
- The same repository instance is always returned for a specific aggregate.
+:::note
+The same repository instance is always returned for a specific aggregate.
+:::
### Event Bus
@@ -60,20 +61,21 @@ $repositoryManager = new DefaultRepositoryManager(
$repository = $repositoryManager->get(Profile::class);
```
-!!! warning
- If you use the event bus, you should be aware that the events are dispatched synchronously.
- You may encounter [at least once](https://softwaremill.com/message-delivery-and-deduplication-strategies/) problems.
+:::warning
+If you use the event bus, you should be aware that the events are dispatched synchronously.
+You may encounter [at least once](https://softwaremill.com/message-delivery-and-deduplication-strategies/) problems.
+:::
-!!! note
-
- You can find out more about event bus [here](event_bus.md).
-
-!!! tip
-
- In most cases it is better to react to events asynchronously,
- that's why we recommend the subscription engine.
- More information can be found [here](subscription.md).
+:::note
+You can find out more about event bus [here](event-bus.md).
+:::
+
+:::tip
+In most cases it is better to react to events asynchronously,
+that's why we recommend the subscription engine.
+More information can be found [here](subscription.md).
+:::
### Snapshots
@@ -105,9 +107,9 @@ $repositoryManager = new DefaultRepositoryManager(
$repository = $repositoryManager->get(Profile::class);
```
-!!! note
-
- You can find out more about snapshots [here](snapshots.md).
+:::note
+You can find out more about snapshots [here](snapshots.md).
+:::
### Decorator
@@ -134,13 +136,14 @@ $repositoryManager = new DefaultRepositoryManager(
$repository = $repositoryManager->get(Profile::class);
```
-!!! note
- You can find out more about message decorator [here](message_decorator.md).
+:::note
+You can find out more about message decorator [here](message-decorator.md).
+:::
-!!! tip
-
- If you have multiple decorators, you can use the `ChainMessageDecorator` to chain them.
+:::tip
+If you have multiple decorators, you can use the `ChainMessageDecorator` to chain them.
+:::
## Use the repository
@@ -163,24 +166,25 @@ $profile = Profile::create($id, 'david.badura@patchlevel.de');
/** @var Repository $repository */
$repository->save($profile);
```
-!!! warning
- All events are written to the database with one transaction in order to ensure data consistency.
- If an exception occurs during the save process,
- the transaction is rolled back and the aggregate is not valid anymore.
- You can not save the aggregate again and you need to load it again.
+:::warning
+All events are written to the database with one transaction in order to ensure data consistency.
+If an exception occurs during the save process,
+the transaction is rolled back and the aggregate is not valid anymore.
+You can not save the aggregate again and you need to load it again.
+:::
-!!! note
+:::note
+Due to the nature of the aggregate having a playhead,
+we have a unique constraint that ensures that no race condition happens here.
+An `AggregateOutdated` exception is thrown if a conflict occurs.
+:::
- Due to the nature of the aggregate having a playhead,
- we have a unique constraint that ensures that no race condition happens here.
- An `AggregateOutdated` exception is thrown if a conflict occurs.
-
-!!! tip
+:::tip
+If you use the Command Bus, you can use the [RetryOutdatedAggregateCommandBus](command-bus.md#retry-outdated-aggregate-command-bus)
+to retry the command when an `AggregateOutdated` exception occurs automatically.
+:::
- If you use the Command Bus, you can use the [RetryOutdatedAggregateCommandBus](command_bus.md#retry-outdated-aggregate-command-bus)
- to retry the command when an `AggregateOutdated` exception occurs automatically.
-
### Load an aggregate
An `aggregate` can be loaded using the `load` method.
@@ -195,15 +199,16 @@ $id = Uuid::fromString('229286ff-6f95-4df6-bc72-0a239fe7b284');
/** @var Repository $repository */
$profile = $repository->load($id);
```
-!!! warning
- When the method is called, the aggregate is always reloaded and rebuilt from the database.
+:::warning
+When the method is called, the aggregate is always reloaded and rebuilt from the database.
+:::
-!!! note
+:::note
+You can only fetch one aggregate at a time and don't do any complex queries either.
+Projections are used for this purpose.
+:::
- You can only fetch one aggregate at a time and don't do any complex queries either.
- Projections are used for this purpose.
-
### Has an aggregate
You can also check whether an `aggregate` with a certain id exists.
@@ -220,10 +225,11 @@ if ($repository->has($id)) {
// ...
}
```
-!!! note
- The query is fast and does not load any event.
- This means that the state of the aggregate is not rebuild either.
+:::note
+The query is fast and does not load any event.
+This means that the state of the aggregate is not rebuild either.
+:::
## Custom Repository
@@ -271,6 +277,6 @@ class ProfileRepository
* [How to create an event](events.md)
* [How to work with the store](store.md)
* [How to use snapshots](snapshots.md)
-* [How to split streams](split_stream.md)
-* [How to use the event bus](event_bus.md)
+* [How to split streams](split-stream.md)
+* [How to use the event bus](event-bus.md)
* [How to create messages](message.md)
diff --git a/docs/requirements.txt b/docs/requirements.txt
deleted file mode 100644
index 50cbcae59..000000000
--- a/docs/requirements.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-mkdocs==1.6.1
-mike==2.1.3
-markdown==3.10
-mkdocs-material==9.7.0
-
-# Markdown extensions
-Pygments==2.19.2
-pymdown-extensions==10.17.2
-
-# MkDocs plugins
-mkdocs-material-extensions==1.3.1
diff --git a/docs/pages/snapshots.md b/docs/snapshots.md
similarity index 80%
rename from docs/pages/snapshots.md
rename to docs/snapshots.md
index aa1d28e80..a8caa83b6 100644
--- a/docs/pages/snapshots.md
+++ b/docs/snapshots.md
@@ -5,13 +5,13 @@ This is not a problem if there are a few hundred.
But if the number gets bigger at some point, then loading and rebuilding can become slow.
The `snapshot` system can be used to control this.
-!!! tip
-
- Use snapshots only if you have a performance problems,
- because it introduces additional complexity.
+:::tip
+Use snapshots only if you have a performance problems,
+because it introduces additional complexity.
- In our benchmarks we can load 10 000 events for one aggregate in 50ms.
- Of course, this can vary from system to system.
+In our benchmarks we can load 10 000 events for one aggregate in 50ms.
+Of course, this can vary from system to system.
+:::
Normally, the events are all applied again on the aggregate in order to rebuild the current state.
With a `snapshot`, we can shorten the way in which we temporarily save the current state of the aggregate.
@@ -55,9 +55,10 @@ $repositoryManager = new DefaultRepositoryManager(
$snapshotStore,
);
```
-!!! note
- You can read more about Repository [here](./repository.md).
+:::note
+You can read more about Repository [here](./repository.md).
+:::
Next we need to tell the Aggregate to take a snapshot of it. We do this using the snapshot attribute.
There we also specify where it should be saved.
@@ -99,19 +100,20 @@ final class Profile extends BasicAggregateRoot
// ...
}
```
-!!! danger
- If anything changes in the properties of the aggregate, then the cache must be cleared.
- Or the snapshot version needs to be changed so that the previous snapshot is invalid.
+:::danger
+If anything changes in the properties of the aggregate, then the cache must be cleared.
+Or the snapshot version needs to be changed so that the previous snapshot is invalid.
+:::
-!!! warning
-
- In the end it the complete aggregate must be serializeable as json, also the aggregate Id.
+:::warning
+In the end it the complete aggregate must be serializeable as json, also the aggregate Id.
+:::
-!!! note
-
- The [hydrator](https://github.com/patchlevel/hydrator) is used internally and you can use all of its features.
- You can find more about normalizer also [here](normalizer.md).
+:::note
+The [hydrator](https://github.com/patchlevel/hydrator) is used internally and you can use all of its features.
+You can find more about normalizer also [here](normalizer.md).
+:::
### Snapshot batching
@@ -154,17 +156,18 @@ final class Profile extends BasicAggregateRoot
// ...
}
```
-!!! warning
- If the snapshots are discarded, a load peak can occur since the aggregates have to be rebuilt.
- You should update the snapshot version only when necessary.
+:::warning
+If the snapshots are discarded, a load peak can occur since the aggregates have to be rebuilt.
+You should update the snapshot version only when necessary.
+:::
-!!! tip
-
- If you have aggregates with a lot of events,
- you should consider using [split streams](split_stream.md) if it make sense in your domain.
- Then the load peak is not so high anymore,
- because only the events from new stream start are loaded to rebuild the aggregate.
+:::tip
+If you have aggregates with a lot of events,
+you should consider using [split streams](split-stream.md) if it make sense in your domain.
+Then the load peak is not so high anymore,
+because only the events from new stream start are loaded to rebuild the aggregate.
+:::
## Adapter
@@ -229,11 +232,12 @@ use Patchlevel\EventSourcing\Snapshot\SnapshotStore;
*/
$snapshotStore->save($aggregate);
```
-!!! danger
- If the state of an aggregate is saved as a snapshot without being saved to the event store (database),
- it can lead to data loss or broken aggregates!
-
+:::danger
+If the state of an aggregate is saved as a snapshot without being saved to the event store (database),
+it can lead to data loss or broken aggregates!
+:::
+
### Load
You can also load an aggregate from the snapshot store:
@@ -251,14 +255,14 @@ The method returns the Aggregate if it was loaded successfully.
If the aggregate was not found, then a `SnapshotNotFound` is thrown.
And if the version is no longer correct and the snapshot is therefore invalid, then a `SnapshotVersionInvalid` is thrown.
-!!! warning
-
- The aggregate may be in an old state as the snapshot may lag behind.
- You still have to bring the aggregate up to date by loading the missing events from the event store.
+:::warning
+The aggregate may be in an old state as the snapshot may lag behind.
+You still have to bring the aggregate up to date by loading the missing events from the event store.
+:::
## Learn more
* [How to define aggregates](aggregate.md)
* [How to store and load aggregates](repository.md)
-* [How to split streams](split_stream.md)
-* [How to work with personal data](personal_data.md)
+* [How to split streams](split-stream.md)
+* [How to work with personal data](personal-data.md)
diff --git a/docs/pages/split_stream.md b/docs/split-stream.md
similarity index 79%
rename from docs/pages/split_stream.md
rename to docs/split-stream.md
index e1237160f..11fd74896 100644
--- a/docs/pages/split_stream.md
+++ b/docs/split-stream.md
@@ -40,13 +40,14 @@ $repositoryManager = new DefaultRepositoryManager(
new SplitStreamDecorator($eventMetadataFactory),
);
```
-!!! note
- You can find out more about decorator [here](./message_decorator.md).
+:::note
+You can find out more about decorator [here](./message-decorator.md).
+:::
-!!! tip
-
- You can use multiple decorators with the `ChainMessageDecorator`.
+:::tip
+You can use multiple decorators with the `ChainMessageDecorator`.
+:::
## Usage
@@ -69,22 +70,23 @@ final class BalanceReported
}
}
```
-!!! warning
- The event needs all data which is relevant the aggregate to be used since all past event will not be loaded!
- Keep this in mind if you want to use this feature.
+:::warning
+The event needs all data which is relevant the aggregate to be used since all past event will not be loaded!
+Keep this in mind if you want to use this feature.
+:::
-!!! note
-
- This impacts only the aggregate loaded by the repository. Subscriptions will still receive all events.
+:::note
+This impacts only the aggregate loaded by the repository. Subscriptions will still receive all events.
+:::
-!!! tip
-
- You can combine this feature with the snapshot feature to increase the performance even more.
+:::tip
+You can combine this feature with the snapshot feature to increase the performance even more.
+:::
## Learn more
-* [How to use message decorator](message_decorator.md)
+* [How to use message decorator](message-decorator.md)
* [How to define events](events.md)
* [How to define aggregates](aggregate.md)
* [How to store and load aggregates](repository.md)
diff --git a/docs/pages/store.md b/docs/store.md
similarity index 88%
rename from docs/pages/store.md
rename to docs/store.md
index bd2ce2b47..2a49a411b 100644
--- a/docs/pages/store.md
+++ b/docs/store.md
@@ -3,9 +3,9 @@
In the end, the messages have to be saved somewhere.
Each message contains an event and the associated headers.
-!!! note
-
- More information about the message can be found [here](message.md).
+:::note
+More information about the message can be found [here](message.md).
+:::
The store is optimized to efficiently store and load events for aggregates.
@@ -33,10 +33,11 @@ $store = new StreamDoctrineDbalStore(
DefaultEventSerializer::createFromPaths(['src/Event']),
);
```
-!!! note
- You can find out more about how to create a connection
- [here](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html)
+:::note
+You can find out more about how to create a connection
+[here](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html)
+:::
Following options are available in `StreamDoctrineDbalStore`:
@@ -72,9 +73,10 @@ use Patchlevel\EventSourcing\Store\InMemoryStore;
$store = new InMemoryStore();
```
-!!! tip
- You can pass messages to the constructor to initialize the store with some events.
+:::tip
+You can pass messages to the constructor to initialize the store with some events.
+:::
### StreamReadOnlyStore
@@ -93,9 +95,9 @@ $readOnlyStore = new ReadOnlyStore($store);
With the help of the `SchemaDirector`, the database structure can be created, updated and deleted.
-!!! tip
-
- You can also use doctrine migration to create and keep your schema in sync.
+:::tip
+You can also use doctrine migration to create and keep your schema in sync.
+:::
### Doctrine Schema Director
@@ -117,9 +119,10 @@ $schemaDirector = new DoctrineSchemaDirector(
$store,
);
```
-!!! note
- How to setup cli commands for schema director can be found [here](cli.md).
+:::note
+How to setup cli commands for schema director can be found [here](cli.md).
+:::
#### Create schema
@@ -221,14 +224,15 @@ $dependencyFactory->setService(
$schemaProvider,
);
```
-!!! note
- Here you can find more information on how to
- [configure doctrine migration](https://www.doctrine-project.org/projects/doctrine-migrations/en/3.3/reference/custom-configuration.html).
+:::note
+Here you can find more information on how to
+[configure doctrine migration](https://www.doctrine-project.org/projects/doctrine-migrations/en/3.3/reference/custom-configuration.html).
+:::
-!!! note
-
- How to setup cli commands for doctrine migration can be found [here](cli.md).
+:::note
+How to setup cli commands for doctrine migration can be found [here](cli.md).
+:::
## Usage
@@ -313,14 +317,15 @@ foreach ($stream as $message) {
$message->event(); // get the event
}
```
-!!! note
- You can find more information about the `Message` object [here](message.md).
+:::note
+You can find more information about the `Message` object [here](message.md).
+:::
-!!! warning
-
- The stream cannot rewind, so you can only iterate over it once.
- If you want to iterate over it again, you have to call the `load` method again.
+:::warning
+The stream cannot rewind, so you can only iterate over it once.
+If you want to iterate over it again, you have to call the `load` method again.
+:::
### Count
@@ -363,14 +368,15 @@ $store->save($message);
$store->save($message1, $message2, $message3);
$store->save(...$messages);
```
-!!! note
- The saving happens in a transaction, so all messages are saved or none.
- The store lock the table for writing during each save by default.
+:::note
+The saving happens in a transaction, so all messages are saved or none.
+The store lock the table for writing during each save by default.
+:::
-!!! tip
-
- Use transactional method if you want call multiple save methods in a transaction.
+:::tip
+Use transactional method if you want call multiple save methods in a transaction.
+:::
### Update
@@ -417,14 +423,15 @@ $store->transactional(static function () use ($command, $bankAccountRepository):
$bankAccountRepository->save($accountTo);
});
```
-!!! note
- The store lock the table for writing during the transaction by default.
+:::note
+The store lock the table for writing during the transaction by default.
+:::
-!!! tip
-
- If you want save only one aggregate, so you don't have to use the transactional method.
- The save method in store/repository is already transactional.
+:::tip
+If you want save only one aggregate, so you don't have to use the transactional method.
+The save method in store/repository is already transactional.
+:::
## Learn more
diff --git a/docs/pages/subscription.md b/docs/subscription.md
similarity index 87%
rename from docs/pages/subscription.md
rename to docs/subscription.md
index 94a4457db..1fc32ab20 100644
--- a/docs/pages/subscription.md
+++ b/docs/subscription.md
@@ -26,16 +26,17 @@ final class DoStuffSubscriber
{
}
```
-!!! note
- For each subsciber ID, the engine will create a subscription.
- If the subscriber ID changes, a new subscription will be created.
- In some cases like projections, you want to change the subscriber ID to rebuild the projection.
+:::note
+For each subsciber ID, the engine will create a subscription.
+If the subscriber ID changes, a new subscription will be created.
+In some cases like projections, you want to change the subscriber ID to rebuild the projection.
+:::
-!!! tip
-
- You can use specific attributes for specific subscribers like `Projector` or `Processor`.
- So you don't have to define the group and run mode every time.
+:::tip
+You can use specific attributes for specific subscribers like `Projector` or `Processor`.
+So you don't have to define the group and run mode every time.
+:::
### Projector
@@ -73,16 +74,17 @@ final class ProfileProjector
}
}
```
-!!! warning
- MySQL and MariaDB don't support transactions for DDL statements.
- So you must use a different database connection for your subscriptions.
+:::warning
+MySQL and MariaDB don't support transactions for DDL statements.
+So you must use a different database connection for your subscriptions.
+:::
-!!! tip
-
- Add a version as suffix to the subscriber id
- so you can increment it when the subscription changes.
- Like `profile_1` to `profile_2`.
+:::tip
+Add a version as suffix to the subscriber id
+so you can increment it when the subscription changes.
+Like `profile_1` to `profile_2`.
+:::
### Processor
@@ -147,10 +149,11 @@ final class DoStuffSubscriber
}
}
```
-!!! tip
- If you are using psalm then you can install the event sourcing [plugin](https://github.com/patchlevel/event-sourcing-psalm-plugin)
- to make the event method return the correct type.
+:::tip
+If you are using psalm then you can install the event sourcing [plugin](https://github.com/patchlevel/event-sourcing-psalm-plugin)
+to make the event method return the correct type.
+:::
### Subscribe all events
@@ -271,9 +274,10 @@ final class PublicProfileProjection
// ... setup, teardown, ...
}
```
-!!! note
- More about reducers you can find [here](./message.md#reducer)
+:::note
+More about reducers you can find [here](./message.md#reducer)
+:::
##### Recorded On Resolver
@@ -335,23 +339,24 @@ final class ProfileProjector
}
}
```
-!!! danger
- MySQL and MariaDB don't support transactions for DDL statements.
- So you must use a different database connection in your projectors,
- otherwise you will get an error when the subscription tries to create the table.
+:::danger
+MySQL and MariaDB don't support transactions for DDL statements.
+So you must use a different database connection in your projectors,
+otherwise you will get an error when the subscription tries to create the table.
+:::
-!!! warning
-
- If you change the subscriber id, you must also change the table/collection name.
- The subscription engine will create a new subscription with the new subscriber id.
- That means the setup method will be called again and the table/collection will conflict with the old existing projection.
- You can use the `SubscriberUtil` to build the table/collection name.
+:::warning
+If you change the subscriber id, you must also change the table/collection name.
+The subscription engine will create a new subscription with the new subscriber id.
+That means the setup method will be called again and the table/collection will conflict with the old existing projection.
+You can use the `SubscriberUtil` to build the table/collection name.
+:::
-!!! note
-
- Most databases have a limit on the length of the table/collection name.
- The limit is usually 64 characters.
+:::note
+Most databases have a limit on the length of the table/collection name.
+The limit is usually 64 characters.
+:::
### On Failed
@@ -384,13 +389,14 @@ final class InvoiceProcessor
}
}
```
-!!! warning
- Currently, the `OnFailed` method is only available for non-batchable subscribers.
+:::warning
+Currently, the `OnFailed` method is only available for non-batchable subscribers.
+:::
-!!! note
-
- The `OnFailed` method is called after the retry strategy has decided that the subscription should be set to failed.
+:::note
+The `OnFailed` method is called after the retry strategy has decided that the subscription should be set to failed.
+:::
### Versioning
@@ -407,16 +413,17 @@ final class ProfileSubscriber
// ...
}
```
-!!! warning
- If you change the `subscriberID`, you must also change the table/collection name.
- Otherwise the table/collection will conflict with the old subscription.
+:::warning
+If you change the `subscriberID`, you must also change the table/collection name.
+Otherwise the table/collection will conflict with the old subscription.
+:::
-!!! tip
-
- Add a version as suffix to the subscriber id
- so you can increment it when the subscription changes.
- Like `profile_1` to `profile_2`.
+:::tip
+Add a version as suffix to the subscriber id
+so you can increment it when the subscription changes.
+Like `profile_1` to `profile_2`.
+:::
### Grouping
@@ -433,13 +440,14 @@ final class ProfileSubscriber
// ...
}
```
-!!! note
- The different attributes has different default group.
+:::note
+The different attributes has different default group.
- * `Subscriber` - `default`
- * `Projector` - `projector`
- * `Processor` - `processor`
+* `Subscriber` - `default`
+* `Projector` - `projector`
+* `Processor` - `processor`
+:::
### Run Mode
@@ -461,9 +469,10 @@ final class WelcomeEmailSubscriber
// ...
}
```
-!!! tip
- If you want create projections and run from the beginning, you can use the `Projector` attribute.
+:::tip
+If you want create projections and run from the beginning, you can use the `Projector` attribute.
+:::
#### From Now
@@ -481,9 +490,10 @@ final class WelcomeEmailSubscriber
// ...
}
```
-!!! tip
- If you want process events from now, you can use the `Processor` attribute.
+:::tip
+If you want process events from now, you can use the `Processor` attribute.
+:::
#### Once
@@ -603,20 +613,20 @@ 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.
-!!! danger
-
- Make sure to fully process the data in `commitBatch` and close any open transactions.
- Otherwise, it may lead to inconsistent data.
+:::danger
+Make sure to fully process the data in `commitBatch` and close any open transactions.
+Otherwise, it may lead to inconsistent data.
+:::
-!!! note
-
- The position of the subscriber is only updated after a successful commit.
- In case of an error, the position remains at the state before the batch started.
+:::note
+The position of the subscriber is only updated after a successful commit.
+In case of an error, the position remains at the state before the batch started.
+:::
-!!! tip
-
- Use `forceCommit` to prevent memory leaks.
- This allows you to decide when it's suitable to process the data and then release the memory.
+:::tip
+Use `forceCommit` to prevent memory leaks.
+This allows you to decide when it's suitable to process the data and then release the memory.
+:::
## Subscription Engine
@@ -627,12 +637,12 @@ and keeping all subscriptions up to date.
He also takes care that new subscribers are booted and old ones are removed again.
If something breaks, the subscription engine marks the individual subscriptions as faulty and retries them.
-!!! tip
-
- The Subscription Engine was inspired by the following two blog posts:
+:::tip
+The Subscription Engine was inspired by the following two blog posts:
- * [Projection Building Blocks: What you'll need to build projections](https://barryosull.com/blog/projection-building-blocks-what-you-ll-need-to-build-projections/)
- * [Managing projectors is harder than you think](https://barryosull.com/blog/managing-projectors-is-harder-than-you-think/)
+* [Projection Building Blocks: What you'll need to build projections](https://barryosull.com/blog/projection-building-blocks-what-you-ll-need-to-build-projections/)
+* [Managing projectors is harder than you think](https://barryosull.com/blog/managing-projectors-is-harder-than-you-think/)
+:::
## Subscription ID
@@ -762,9 +772,9 @@ The subscription engine needs a message loader to load the messages.
We provide two implementations by default.
Which one has a better performance depends on the use case.
-!!! tip
-
- We recommend the `GapResolverStoreMessageLoader` as it handles gaps in the stream.
+:::tip
+We recommend the `GapResolverStoreMessageLoader` as it handles gaps in the stream.
+:::
#### Store Message Loader
@@ -862,9 +872,10 @@ $schemaDirector = new DoctrineSchemaDirector(
]),
);
```
-!!! note
- You can find more about schema configurator [here](./store.md)
+:::note
+You can find more about schema configurator [here](./store.md)
+:::
### Retry Strategy
@@ -920,13 +931,14 @@ $retryStrategyRepository = new RetryStrategyRepository([
'no_retry' => new NoRetryStrategy(),
]);
```
-!!! note
- This is what our default configuration looks like if you do not define the retry strategy.
+:::note
+This is what our default configuration looks like if you do not define the retry strategy.
+:::
-!!! tip
-
- You can change the default retry strategy by define the name in the constructor as second parameter.
+:::tip
+You can change the default retry strategy by define the name in the constructor as second parameter.
+:::
### Subscriber Accessor
@@ -988,9 +1000,10 @@ use Patchlevel\EventSourcing\Subscription\Engine\SubscriptionEngine;
/** @var SubscriptionEngine $subscriptionEngine */
$catchupSubscriptionEngine = new CatchUpSubscriptionEngine($subscriptionEngine);
```
-!!! tip
- You can use the `CatchUpSubscriptionEngine` in your tests to process the events immediately.
+:::tip
+You can use the `CatchUpSubscriptionEngine` in your tests to process the events immediately.
+:::
### Throw on error Subscription Engine
@@ -1004,10 +1017,11 @@ use Patchlevel\EventSourcing\Subscription\Engine\ThrowOnErrorSubscriptionEngine;
/** @var SubscriptionEngine $subscriptionEngine */
$throwOnErrorSubscriptionEngine = new ThrowOnErrorSubscriptionEngine($subscriptionEngine);
```
-!!! warning
- This is only for testing or development. Don't use it in production.
- The subscription engine has an build in retry strategy to retry subscriptions that have failed.
+:::warning
+This is only for testing or development. Don't use it in production.
+The subscription engine has an build in retry strategy to retry subscriptions that have failed.
+:::
### Run Subscription Engine after save
@@ -1031,20 +1045,21 @@ $eventBus = new RunSubscriptionEngineRepositoryManager(
100, // limit the number of messages
);
```
-!!! danger
- By using this, you can't wrap the repository in a transaction.
- A rollback is not supported and can break the subscription engine.
- Internally, the events are saved in a transaction to ensure data consistency.
+:::danger
+By using this, you can't wrap the repository in a transaction.
+A rollback is not supported and can break the subscription engine.
+Internally, the events are saved in a transaction to ensure data consistency.
+:::
-!!! note
-
- More about repository manager and repository can be found [here](./repository.md).
+:::note
+More about repository manager and repository can be found [here](./repository.md).
+:::
-!!! tip
-
- You can perfectly use it in development or testing.
- Especially in combination with the `CatchUpSubscriptionEngine` and `ThrowOnErrorSubscriptionEngine` decorators.
+:::tip
+You can perfectly use it in development or testing.
+Especially in combination with the `CatchUpSubscriptionEngine` and `ThrowOnErrorSubscriptionEngine` decorators.
+:::
## Usage
@@ -1059,9 +1074,10 @@ $criteria = new SubscriptionEngineCriteria(
groups: ['default'],
);
```
-!!! note
- An `OR` check is made for the respective criteria and all criteria are checked with an `AND`.
+:::note
+An `OR` check is made for the respective criteria and all criteria are checked with an `AND`.
+:::
### Setup
@@ -1076,9 +1092,10 @@ use Patchlevel\EventSourcing\Subscription\Engine\SubscriptionEngineCriteria;
/** @var SubscriptionEngine $subscriptionEngine */
$subscriptionEngine->setup(new SubscriptionEngineCriteria());
```
-!!! tip
- You can skip the booting step with the second boolean parameter named `skipBooting`.
+:::tip
+You can skip the booting step with the second boolean parameter named `skipBooting`.
+:::
### Boot
diff --git a/docs/pages/supported-versions.md b/docs/supported-versions.md
similarity index 92%
rename from docs/pages/supported-versions.md
rename to docs/supported-versions.md
index 76d7b4c04..b228a22e4 100644
--- a/docs/pages/supported-versions.md
+++ b/docs/supported-versions.md
@@ -17,4 +17,4 @@ not be used anymore.
## Versioning
For more information about our versioning you should
-read [our backward compatibility promise](./our-backward-compatibility-promise.md).
+read [our backward compatibility promise](./backward-compatibility.md).
diff --git a/docs/pages/testing.md b/docs/testing.md
similarity index 100%
rename from docs/pages/testing.md
rename to docs/testing.md
diff --git a/docs/pages/upcasting.md b/docs/upcasting.md
similarity index 100%
rename from docs/pages/upcasting.md
rename to docs/upcasting.md