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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
composer.lock
vendor
vendor
.phpunit.cache
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,37 @@
# subscription-engine
# Subscription engine for Event Stores

Subscription engine for event-sourced systems

## Usage

This package contains base types and interfaces for Subscription Engines.
In order to use it, implementations for the following interfaces have to be provided:

### `SubscriptionStore`

The [SubscriptionStore](./src/Store/SubscriptionStore.php) allows to persist and update the state of subscriptions and allows to lock access such that multiple processes cannot change the same subscription at the same time.

#### Implementations

- [wwwision/subscription-engine-doctrine](https://packagist.org/packages/wwwision/subscription-engine-doctrine) provides a DBAL implementation that is compatible with MySQL/MariaDB, PostgreSQL and SQLite.

### `EventStoreAdapter`

The [EventStoreAdapter](./src/EventStore/EventStoreAdapter.php) is the adapter for the actual event store implementation

#### Implementations

- [wwwision/subscription-engine-neos-adapter](https://packagist.org/packages/wwwision/subscription-engine-neos-adapter) - adapter for the [neos/eventstore](https://packagist.org/packages/neos/eventstore)
- [wwwision/subscription-engine-dcb-adapter](https://packagist.org/packages/wwwision/subscription-engine-dcb-adapter) - adapter for the [wwwision/dcb-eventstore](https://packagist.org/packages/wwwision/dcb-eventstore)

## Contribution

Contributions in the form of [issues](https://github.com/bwaidelich/subscription-engine/issues) or [pull requests](https://github.com/bwaidelich/subscription-engine/pulls) are highly appreciated

## License

See [LICENSE](./LICENSE)

## Acknowledgements

This implementation is heavily inspired by parts of the [patchlevel/event-sourcing](https://packagist.org/packages/patchlevel/event-sourcing) package
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@
"test:phpstan": "phpstan",
"test:cs": "phpcs --colors src",
"test:cs:fix": "phpcbf --colors src",
"test:phpunit": "phpunit",
"test": [
"@test:phpstan",
"@test:cs"
"@test:cs",
"@test:phpunit"
]
}
}
35 changes: 12 additions & 23 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd"
bootstrap="vendor/autoload.php"
cacheDirectory=".phpunit.cache"
executionOrder="depends,defects"
requireCoverageMetadata="true"
beStrictAboutCoverageMetadata="true"
beStrictAboutOutputDuringTests="true"
failOnRisky="true"
failOnWarning="true">
<testsuites>
<testsuite name="default">
<directory>tests/unit</directory>
</testsuite>
</testsuites>

<coverage includeUncoveredFiles="false" />

<source restrictDeprecations="true" restrictNotices="true" restrictWarnings="true">
<include>
<directory>src</directory>
</include>
</source>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd" bootstrap="vendor/autoload.php" cacheDirectory=".phpunit.cache" executionOrder="depends,defects" requireCoverageMetadata="true" beStrictAboutCoverageMetadata="false" beStrictAboutOutputDuringTests="true" failOnRisky="true" failOnWarning="true">
<testsuites>
<testsuite name="default">
<directory>tests/PHPUnit</directory>
</testsuite>
</testsuites>
<coverage/>
<source restrictNotices="true" restrictWarnings="true" ignoreIndirectDeprecations="true">
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
29 changes: 29 additions & 0 deletions src/Engine/EngineEvent/ActiveSubscriptionSetup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Wwwision\SubscriptionEngine\Subscriber\Subscriber;
use Wwwision\SubscriptionEngine\Subscription\Subscription;

/**
* @implements EngineEvent<void>
*/
final readonly class ActiveSubscriptionSetup implements EngineEvent
{
public function __construct(
public Subscription $subscription,
) {
}

public function logLevel(): int
{
return LOG_DEBUG;
}

public function __toString(): string
{
return sprintf('Active subscriber for "%s" has been re-setup', $this->subscription->id->value);
}
}
36 changes: 36 additions & 0 deletions src/Engine/EngineEvent/CatchUpFinished.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Wwwision\SubscriptionEngine\Engine\Errors;
use Wwwision\SubscriptionEngine\Subscription\Subscriptions;

/**
* @implements EngineEvent<void>
*/
final readonly class CatchUpFinished implements EngineEvent
{
public function __construct(
public Subscriptions $subscriptions,
public int $numberOfProcessedEvents,
public Errors|null $errors,
) {
}

public function logLevel(): int
{
return LOG_DEBUG;
}

public function __toString(): string
{
$subscriptionsCount = $this->subscriptions->count();
if ($this->errors !== null) {
$errorCount = $this->errors->count();
return sprintf('Finished catch-up of %d subscription%s, processed %d event%s (%d error%s)', $subscriptionsCount, $subscriptionsCount > 1 ? 's' : '', $this->numberOfProcessedEvents, $this->numberOfProcessedEvents > 1 ? 's' : '', $errorCount, $errorCount > 1 ? 's' : '');
}
return sprintf('Finished catch-up of %d subscription%s, processed %d event%s (no errors)', $subscriptionsCount, $subscriptionsCount > 1 ? 's' : '', $this->numberOfProcessedEvents, $this->numberOfProcessedEvents > 1 ? 's' : '');
}
}
30 changes: 30 additions & 0 deletions src/Engine/EngineEvent/CatchUpInitiated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Wwwision\SubscriptionEngine\Engine\SubscriptionEngineCriteria;
use Wwwision\SubscriptionEngine\Subscription\SubscriptionStatusFilter;

/**
* @implements EngineEvent<void>
*/
final readonly class CatchUpInitiated implements EngineEvent
{
public function __construct(
public SubscriptionEngineCriteria $criteria,
public SubscriptionStatusFilter $status
) {
}

public function logLevel(): int
{
return LOG_DEBUG;
}

public function __toString(): string
{
return sprintf('Initiated catch-up of subscriptions in states %s', implode(',', $this->status->toStringArray()));
}
}
33 changes: 33 additions & 0 deletions src/Engine/EngineEvent/CatchUpStarted.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Wwwision\SubscriptionEngine\Engine\SubscriptionEngineCriteria;
use Wwwision\SubscriptionEngine\Subscription\Position;
use Wwwision\SubscriptionEngine\Subscription\Subscriptions;
use Wwwision\SubscriptionEngine\Subscription\SubscriptionStatusFilter;

/**
* @implements EngineEvent<void>
*/
final readonly class CatchUpStarted implements EngineEvent
{
public function __construct(
public Subscriptions $subscriptions,
public Position $startPosition,
) {
}

public function logLevel(): int
{
return LOG_INFO;
}

public function __toString(): string
{
$subscriptionsCount = $this->subscriptions->count();
return sprintf('Starting catch-up of %d subscription%s from position %d', $subscriptionsCount, $subscriptionsCount > 1 ? 's' : '', $this->startPosition->value);
}
}
18 changes: 18 additions & 0 deletions src/Engine/EngineEvent/EngineEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Stringable;

/**
* @template-covariant E of object|void
*/
interface EngineEvent extends Stringable
{
/**
* @return int one of PHPs LOG_* constants
*/
public function logLevel(): int;
}
30 changes: 30 additions & 0 deletions src/Engine/EngineEvent/FailedToResetSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Throwable;
use Wwwision\SubscriptionEngine\Subscription\Subscription;

/**
* @implements EngineEvent<void>
*/
final readonly class FailedToResetSubscriber implements EngineEvent
{
public function __construct(
public Subscription $subscription,
public Throwable $exception,
) {
}

public function logLevel(): int
{
return LOG_ERR;
}

public function __toString(): string
{
return sprintf('Subscriber for "%s" has an error in the reset method: %s', $this->subscription->id->value, $this->exception->getMessage());
}
}
30 changes: 30 additions & 0 deletions src/Engine/EngineEvent/FailedToSetupSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Throwable;
use Wwwision\SubscriptionEngine\Subscription\Subscription;

/**
* @implements EngineEvent<void>
*/
final readonly class FailedToSetupSubscriber implements EngineEvent
{
public function __construct(
public Subscription $subscription,
public Throwable $exception,
) {
}

public function logLevel(): int
{
return LOG_ERR;
}

public function __toString(): string
{
return sprintf('Subscriber for "%s" has an error in the setup method: %s', $this->subscription->id->value, $this->exception->getMessage());
}
}
29 changes: 29 additions & 0 deletions src/Engine/EngineEvent/NoResetResetFunctionDefined.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Wwwision\SubscriptionEngine\Subscription\Subscription;

/**
* @implements EngineEvent<void>
*/
final readonly class NoResetResetFunctionDefined implements EngineEvent
{

public function __construct(
public Subscription $subscription,
) {
}

public function logLevel(): int
{
return LOG_DEBUG;
}

public function __toString(): string
{
return sprintf('No reset function defined for subscriber for "%s"', $this->subscription->id->value);
}
}
28 changes: 28 additions & 0 deletions src/Engine/EngineEvent/NoSubscriptionsFound.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

use Wwwision\SubscriptionEngine\Store\SubscriptionCriteria;

/**
* @implements EngineEvent<void>
*/
final readonly class NoSubscriptionsFound implements EngineEvent
{
public function __construct(
public SubscriptionCriteria $criteria,
) {
}

public function logLevel(): int
{
return LOG_INFO;
}

public function __toString(): string
{
return sprintf('No subscriptions found for criteria: %s', json_encode($this->criteria));
}
}
22 changes: 22 additions & 0 deletions src/Engine/EngineEvent/ResetStarted.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Wwwision\SubscriptionEngine\Engine\EngineEvent;

/**
* @implements EngineEvent<void>
*/
final readonly class ResetStarted implements EngineEvent
{

public function logLevel(): int
{
return LOG_DEBUG;
}

public function __toString(): string
{
return 'Start to setup';
}
}
Loading