PHPackages                             chrisjenkinson/dynamo-db-event-store - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. chrisjenkinson/dynamo-db-event-store

ActiveLibrary[Database &amp; ORM](/categories/database)

chrisjenkinson/dynamo-db-event-store
====================================

Event store for Broadway using DynamoDB

v0.4(2mo ago)04.3k↑213.3%[4 PRs](https://github.com/chrisjenkinson/dynamo-db-event-store/pulls)GPL-3.0-or-laterPHPCI passing

Since Sep 21Pushed 4d ago1 watchersCompare

[ Source](https://github.com/chrisjenkinson/dynamo-db-event-store)[ Packagist](https://packagist.org/packages/chrisjenkinson/dynamo-db-event-store)[ RSS](/packages/chrisjenkinson-dynamo-db-event-store/feed)WikiDiscussions main Synced today

READMEChangelog (4)Dependencies (16)Versions (11)Used By (0)

DynamoDB Event Store
====================

[](#dynamodb-event-store)

A PHP event store implementation for [Broadway](https://github.com/broadway/broadway) using Amazon DynamoDB as the persistence layer.

Overview
--------

[](#overview)

This library provides a Broadway-compatible event store backed by AWS DynamoDB, allowing you to build event-sourced applications with DynamoDB as your event storage. It handles the serialization, normalization, and querying of domain events stored in DynamoDB.

Features
--------

[](#features)

- 🏗️ **Event Sourcing**: Full support for Broadway's event store interface
- 📊 **DynamoDB Integration**: Uses async-aws/dynamo-db for non-blocking DynamoDB operations
- 🔄 **Event Management**: Create, read, and manage event streams by aggregate ID
- 🎯 **Playhead Support**: Query events from a specific playhead (event version)
- ✅ **Type Safety**: Strict PHP typing throughout
- 🧪 **Well Tested**: Includes comprehensive unit tests with mutation testing

Requirements
------------

[](#requirements)

- PHP 8.1 or higher
- Broadway 2.4.0 or later
- Async AWS DynamoDB client (1.3.0+ or 3.0.0+)

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require chrisjenkinson/dynamo-db-event-store
```

Quick Start
-----------

[](#quick-start)

### Setup

[](#setup)

First, create and configure the DynamoDB event store:

```
use AsyncAws\DynamoDb\DynamoDbClient;
use chrisjenkinson\DynamoDbEventStore\DynamoDbEventStore;
use chrisjenkinson\DynamoDbEventStore\DomainMessageNormalizer;
use chrisjenkinson\DynamoDbEventStore\InputBuilder;
use chrisjenkinson\DynamoDbEventStore\JsonDecoder;
use chrisjenkinson\DynamoDbEventStore\JsonEncoder;
use Broadway\Serializer\SimpleInterfaceSerializer;

// Initialize DynamoDB client
$client = new DynamoDbClient([
    'region' => 'us-east-1',
]);

// Create the event store
$inputBuilder = new InputBuilder();
$normalizer = new DomainMessageNormalizer(
    new SimpleInterfaceSerializer(),
    new SimpleInterfaceSerializer(),
    new JsonEncoder(),
    new JsonDecoder()
);

$eventStore = new DynamoDbEventStore(
    $client,
    $inputBuilder,
    $normalizer,
    'events-table', // DynamoDB table name
    aggregateConsistentReads: false, // set true for strongly consistent aggregate reads
    replayPageSize: 1000 // batch size used internally by visitEvents()
);

// Create the table (if it doesn't exist)
$eventStore->createTable();
```

### Table Schema

[](#table-schema)

The event store automatically creates a DynamoDB table with the following structure:

- **Partition Key**: `Id` (String) - The aggregate ID
- **Sort Key**: `Playhead` (Number) - The event version number
- **Billing Mode**: Pay-per-request

Additional attributes written to each event row:

- **Feed** (String) - Always `all`; used as the partition key of the global replay index
- **GlobalPosition** (Number) - Monotonically increasing, assigned atomically at append time

A Global Secondary Index (`Feed-GlobalPosition-index`) on `Feed` (HASH) + `GlobalPosition` (RANGE) enables ordered cross-aggregate replay.

### Usage

[](#usage)

#### Append Events

[](#append-events)

```
use Broadway\Domain\DomainEventStream;

$eventStream = new DomainEventStream([
    // your domain events
]);

$eventStore->append($aggregateId, $eventStream);
```

#### Load Events

[](#load-events)

```
// Load all events for an aggregate
$eventStream = $eventStore->load($aggregateId);

// Load events from a specific playhead onwards
$eventStream = $eventStore->loadFromPlayhead($aggregateId, $playhead);
```

#### Visit Events

[](#visit-events)

```
use Broadway\EventStore\Management\Criteria;

$eventStore->visitEvents(new Criteria(), $eventVisitor);
```

Events are visited in `GlobalPosition` order, so cross-aggregate replay reflects the exact interleaving in which events were appended.

#### Load Replay Pages

[](#load-replay-pages)

```
use Broadway\EventStore\Management\Criteria;

$page = $eventStore->loadReplayPageAfterGlobalPosition(Criteria::create(), $checkpoint, 500);

foreach ($page->events() as $event) {
    $projector->apply($event->message());
}

$checkpoint = $page->lastProcessedGlobalPosition();
$hasMore    = $page->hasMore();
```

`$limit` bounds examined replay rows, not emitted events. `lastProcessedGlobalPosition()` advances across filtered rows, and `hasMore()` reflects DynamoDB continuation state for the bounded query.

Replay Ordering
---------------

[](#replay-ordering)

Aggregate streams are ordered by `Playhead`. Cross-aggregate replay (`visitEvents`, `loadReplayPageAfterGlobalPosition`) uses `GlobalPosition`, assigned atomically per append batch and stored in a DynamoDB Global Secondary Index. Events are always visited in the order they were committed, regardless of aggregate ID.

Read Consistency
----------------

[](#read-consistency)

Aggregate stream reads (`load`, `loadFromPlayhead`) use eventually consistent reads by default. Pass `aggregateConsistentReads: true` to the constructor for strongly consistent aggregate reads:

```
$eventStore = new DynamoDbEventStore($client, $inputBuilder, $normalizer, 'table', aggregateConsistentReads: true);
```

Global replay reads (`visitEvents`, `loadReplayPageAfterGlobalPosition`) are always eventually consistent — DynamoDB Global Secondary Indexes do not support strongly consistent reads.

`visitEvents()` drains replay pages using a constructor-configurable `replayPageSize`, which defaults to `1000`.

Core Components
---------------

[](#core-components)

- **DynamoDbEventStore**: Main event store implementation
- **DomainMessageNormalizer**: Handles serialization/deserialization of domain events
- **InputBuilder**: Constructs DynamoDB query inputs
- **JsonEncoder/JsonDecoder**: JSON serialization utilities

Testing
-------

[](#testing)

The project includes comprehensive tests:

```
# Run all tests
composer run tests

# Run specific test suites
composer run phpunit          # Unit tests
composer run phpstan          # Static analysis
composer run infection        # Mutation testing
```

Development
-----------

[](#development)

### Code Quality

[](#code-quality)

This project maintains high code quality standards:

- **PHPUnit**: Unit testing
- **PHPStan**: Static type checking
- **Easy Coding Standard**: Code style enforcement
- **Infection**: Mutation testing for test coverage validation

### Scripts

[](#scripts)

```
# Run complete test suite
composer run tests

# Run individual checks
composer run phpunit
composer run phpstan
composer run infection
```

Architecture
------------

[](#architecture)

The event store follows Broadway's EventStore interface and includes event management capabilities. Events are serialized to JSON and stored in DynamoDB with metadata, allowing for complete event replay and reconstruction of aggregate state.

License
-------

[](#license)

This project is licensed under the GNU General Public License v3.0. See LICENSE file for details.

Author
------

[](#author)

Chris Jenkinson

Related Projects
----------------

[](#related-projects)

- [Broadway](https://github.com/broadway/broadway) - Event sourcing framework for PHP
- [Async AWS SDK](https://github.com/async-aws/async-aws) - Non-blocking AWS SDK for PHP

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance94

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 70.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~439 days

Total

4

Last Release

64d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3810ae075b824735d2ce95e065d7b43494f19f8cb941913dde34829783a98d88?d=identicon)[chrisjenkinson](/maintainers/chrisjenkinson)

---

Top Contributors

[![chrisjenkinson](https://avatars.githubusercontent.com/u/568142?v=4)](https://github.com/chrisjenkinson "chrisjenkinson (26 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (11 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleECS

Type Coverage Yes

### Embed Badge

![Health badge](/badges/chrisjenkinson-dynamo-db-event-store/health.svg)

```
[![Health](https://phpackages.com/badges/chrisjenkinson-dynamo-db-event-store/health.svg)](https://phpackages.com/packages/chrisjenkinson-dynamo-db-event-store)
```

###  Alternatives

[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k117.2M118](/packages/jdorn-sql-formatter)[propel/propel1

Propel is an open-source Object-Relational Mapping (ORM) for PHP5.

8351.6M87](/packages/propel-propel1)[broadway/event-store-dbal

Event store implementation using doctrine/dbal

29906.6k8](/packages/broadway-event-store-dbal)[jfelder/oracledb

Oracle DB driver for Laravel

11518.4k](/packages/jfelder-oracledb)[broadway/read-model-elasticsearch

Elasticsearch read model implementation using elastic/elasticsearch-php

10178.9k4](/packages/broadway-read-model-elasticsearch)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
