federicozardi/eventing

Framework-agnostic eventing toolkit for PHP: EventBridge publishing, DynamoDB event store, HMAC-secured webhooks.

Maintainers

Package info

bitbucket.org/fzardi/php-eventing

pkg:composer/federicozardi/eventing

Statistics

Installs: 3

Dependents: 1

Suggesters: 0

v1.0.1 2026-04-30 10:36 UTC

This package is not auto-updated.

Last update: 2026-05-01 11:28:37 UTC


README

Framework-agnostic eventing toolkit for PHP:

  • Event store: DynamoDB (append-only + load)
  • Event bus: EventBridge publisher
  • Webhook security: HMAC signer/verifier (portable: Laravel + Opencart)

This package does not provision AWS resources. It assumes DynamoDB tables and EventBridge bus/rules/targets are created via IaC (Terraform/CloudFormation).

Requirements

  • PHP >=8.2
  • AWS credentials via standard AWS SDK chain (env vars, IAM role, instance profile, etc.)

Event envelope (v1)

EventEnvelope fields:

  • eventId (uuid)
  • eventType (versioned string, e.g. order.created.v1)
  • occurredAt (ISO8601)
  • source (service name)
  • correlationId / causationId (optional)
  • aggregate: { type, id, version }
  • payload (object)
  • metadata (object)

HMAC webhook contract

Headers:

  • X-Eventing-Timestamp: unix epoch seconds
  • X-Eventing-Signature: v1=<hex_hmac_sha256>

Signing string:

{timestamp}.{rawBody}

Signature:

hex(hmac_sha256(secret, signing_string))

DynamoDB schema

Event store table

  • PK: AGG#{aggregateType}#{aggregateId}
  • SK: HEAD (version pointer) or V#{versionPadded} (event items)

Inbox table (consumer dedupe)

  • PK: CONSUMER#{consumerName}
  • SK: EVENT#{eventId}
  • TTL: expiresAt

Concurrency / expectedVersion

DynamoDbEventStore::append() supports optimistic concurrency via expectedVersion:

  • expectedVersion = 0: aggregate must not exist yet (no HEAD)
  • expectedVersion > 0: HEAD.currentVersion must match

If you want a simpler “append without tracking versions”, you can disable concurrency checks by passing disableConcurrencyCheck: true to the constructor. In that mode, the store:

  • ignores the expectedVersion argument
  • reads HEAD.currentVersion (via GetItem) and appends after it (best-effort)

Note: disabling concurrency checks can cause lost updates under concurrent writes.

Usage (PHP)

One-call publish (recommended)

use Aws\DynamoDb\DynamoDbClient;
use Aws\EventBridge\EventBridgeClient;
use Amevista\Eventing\EventBus\EventBridgePublisher;
use Amevista\Eventing\EventStore\DynamoDbEventStore;
use Amevista\Eventing\EventStore\DomainEvent;
use Amevista\Eventing\Eventing;

$region = getenv('AWS_DEFAULT_REGION') ?: 'eu-west-3';
$source = getenv('APP_SERVICE') ?: 'template-service';

$ddb = new DynamoDbClient(['version' => 'latest', 'region' => $region]);
$eb = new EventBridgeClient(['version' => 'latest', 'region' => $region]);

$eventStore = new DynamoDbEventStore(
  $ddb,
  getenv('EVENTING_EVENTSTORE_TABLE'),
  $source,
  disableConcurrencyCheck: (bool) (getenv('EVENTING_DISABLE_CONCURRENCY_CHECK') ?: false),
);
$publisher = new EventBridgePublisher($eb, getenv('EVENTING_EVENTBUS_NAME'));

$eventing = new Eventing($eventStore, $publisher);

$eventing->publish('test', 'ping-1', expectedVersion: 0, events: [
  new DomainEvent('test.ping.v1', payload: ['hello' => 'world']),
]);

Append to event store

use Aws\DynamoDb\DynamoDbClient;
use Amevista\Eventing\EventStore\DynamoDbEventStore;
use Amevista\Eventing\EventStore\DomainEvent;

$ddb = new DynamoDbClient([
  'version' => 'latest',
  'region' => 'eu-west-3',
]);

$store = new DynamoDbEventStore($ddb, tableName: 'event_store', source: 'order-service');

$committed = $store->append('order', '123', expectedVersion: 0, events: [
  new DomainEvent('order.created.v1', payload: ['orderId' => 123]),
]);

Publish to EventBridge

use Aws\EventBridge\EventBridgeClient;
use Amevista\Eventing\EventBus\EventBridgePublisher;

$eb = new EventBridgeClient([
  'version' => 'latest',
  'region' => 'eu-west-3',
]);

$publisher = new EventBridgePublisher($eb, eventBusName: 'my-event-bus');
$awsEventId = $publisher->publish($committed[0]);

IAM notes

Minimal DynamoDB permissions:

  • Event store table:
    • dynamodb:PutItem, dynamodb:UpdateItem, dynamodb:ConditionCheckItem (for transactional writes)
    • dynamodb:Query (for load())
    • dynamodb:GetItem (required when disableConcurrencyCheck=true, to read HEAD)
  • Inbox table:
    • dynamodb:PutItem (dedupe record)