PHPackages                             blackfrog/laravel-event-sourcing-dynamodb - 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. blackfrog/laravel-event-sourcing-dynamodb

ActiveLibrary

blackfrog/laravel-event-sourcing-dynamodb
=========================================

An AWS DynamoDB driver for Spatie Laravel Event Sourcing

311[2 PRs](https://github.com/blackfrog-co/laravel-event-sourcing-dynamodb/pulls)PHP

Since Jul 3Pushed 2y agoCompare

[ Source](https://github.com/blackfrog-co/laravel-event-sourcing-dynamodb)[ Packagist](https://packagist.org/packages/blackfrog/laravel-event-sourcing-dynamodb)[ RSS](/packages/blackfrog-laravel-event-sourcing-dynamodb/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (3)Used By (0)

An AWS DynamoDB driver for Spatie Laravel Event Sourcing
========================================================

[](#an-aws-dynamodb-driver-for-spatie-laravel-event-sourcing)

[![Latest Version on Packagist](https://camo.githubusercontent.com/b2ea02984b175ae623ba3ae0070a2de65b4b73da23ef0869f8b9506bb54f4c65/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f626c61636b66726f672f6c61726176656c2d6576656e742d736f757263696e672d64796e616d6f64622e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/blackfrog/laravel-event-sourcing-dynamodb)[![GitHub Tests Action Status](https://camo.githubusercontent.com/66a0028e3a95ed27758ddc30aa258a20a4264fba4e06785c20c990ec7080955a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f626c61636b66726f672d636f2f6c61726176656c2d6576656e742d736f757263696e672d64796e616d6f64622f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/blackfrog-co/laravel-event-sourcing-dynamodb/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/b05398434041324e957c73b12b24461d740f60a04e5a7f430d37ccb35d4dd5be/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f626c61636b66726f672d636f2f6c61726176656c2d6576656e742d736f757263696e672d64796e616d6f64622f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/blackfrog-co/laravel-event-sourcing-dynamodb/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Test Coverage](https://camo.githubusercontent.com/9f7738935af890764166ca03091fa7e3a0f35e74a084dd6230e391cf4fee02e7/68747470733a2f2f636f6465636f762e696f2f67682f626c61636b66726f672d636f2f6c61726176656c2d6576656e742d736f757263696e672d64796e616d6f64622f6272616e63682f6d61696e2f67726170682f62616467652e7376673f746f6b656e3d434f5042315051354354)](https://codecov.io/gh/blackfrog-co/laravel-event-sourcing-dynamodb)[![Total Downloads](https://camo.githubusercontent.com/0588efdb2ec7ce60b223cb97ecf76212e61f4c1a45dc84338851d449244e6739/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f626c61636b66726f672f6c61726176656c2d6576656e742d736f757263696e672d64796e616d6f64622e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/blackfrog/laravel-event-sourcing-dynamodb)

**! Work In Progress !** Not yet suitable for use. Please wait for the first SemVer versioned release.

A DynamoDB driver for [`spatie/laravel-event-sourcing`](https://github.com/spatie/laravel-event-sourcing) allowing for a serverless approach to your event and snapshot data storage.

**TODOs for first release:**

- Review approach to handling event metadata, ensure its compatible.
- Copy and modify any parts of the main package test suite that can give more end to end coverage.

- [Features](#features)
    - [Minor Differences](#minor-differences)
    - [Requirements](#requirements)
- [Should I use DynamoDB?](#should-i-use-dynamodb)
- [How It Works](#how-it-works)
    - [Events](#events)
    - [Snapshots](#snapshots)
    - [Read Consistency](#read-consistency)
    - [Lazy Collections](#lazy-collections)
- [Limitations](#limitations)
    - [DynamoDB](#dynamodb)
    - [Transactions](#transactions)
    - [Event Ids](#event-ids)
- [Getting Started](#getting-started)
    - [Install](#install)
    - [Configure](#configure)
    - [Create DynamoDB Tables](#create-dynamodb-tables)
    - [Update Spatie Laravel Event Sourcing Config](#update-spatie-laravel-event-sourcing-config)
- [Testing](#testing)
- [Local Development](#local-development)
- [Future Roadmap Ideas](#future-roadmap-ideas)
- [Changelog](#changelog)
- [Credits](#credits)
- [License](#license)

Features
--------

[](#features)

- Provides a DynamoDB implementation for `StoredEventRepository` and `SnapshotRepository`.
- Compatibility with the Spatie Eloquent implementations. See [minor differences](#minor-differences).
- Unlimited [snapshot](#snapshots) size.
- CreateTables command to get you started quickly.
- Optional support for [strongly consistent reads](#read-consistency) (with caveats).
- [Lazy Collection support](#lazy-collections), backed by AWS's PHP paginator.

### Minor Differences

[](#minor-differences)

- The default `EloquentStoredEventRepository:store()` implementation converts a `null` `$uuid` argument to an empty string for storage. DynamoDB does not allow empty strings, so we store this as the string `'null'`.
- There's currently no support for `persistInTransaction()` on AggregateRoots, the package doesn't use this method out of the box itself, but you might. [Read More](#transactions).

### Requirements

[](#requirements)

- 64bit PHP 8.2
- `"spatie/laravel-event-sourcing": "^7.3.3"`,

Should I use DynamoDB?
----------------------

[](#should-i-use-dynamodb)

**DynamoDB brings a number of advantages:**

- It's serverless, scaling cost and capacity starting from zero and entirely based on usage, unlike the database solutions supported through Eloquent.
- It's fast and cheap when you stick to the planned access patterns, i.e. using the methods on the Spatie interfaces, and you leverage snapshots regularly to avoid retrieving all events every time.
- It can easily connect you to a wider AWS event sourcing ecosystem, sharing your events with other apps &amp; services via [DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html) and [Event Bridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-pipes-dynamodb.html).
- It pairs perfectly with [Laravel Vapor](https://vapor.laravel.com/), getting rid of any fixed monthly RDS costs.
- Laravel already has official support built in for DynamoDB as both a Cache and Session driver, so you can use those too for a simpler more consistent serverless stack.

**When it's not right for you:**

- You want to avoid lock in to AWS as a cloud vendor.
- You want to add many custom queries for Events or Snapshots that the package doesn't currently offer.
- You want to be able to do complex analytics and queries on your DynamoDB event tables in the future. This could get expensive and slow. [Fathom Analytics struggled with this problem](https://usefathom.com/blog/ditched-dynamodb).
- You don't want to invest time into understanding DynamoDB and its strengths and weaknesses.
- You haven't read the whole of this README!

How It Works
------------

[](#how-it-works)

### Events

[](#events)

- Events are stored in a table with `aggregate_uuid` as `HASH` (partition) key and `id` as `RANGE` key.
- The events table has two indexes to cover the behaviours of the `StoredEventRepository` interface.
- A [Global Secondary Index](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html) (projects all attributes) that has the `id` as both the `HASH` and `RANGE` keys supports fetching events without their aggregate uuid while preserving their order, both `find($id)` and `retrieveAll(null)` use this.
- A [Local Secondary Index](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html) (projects keys only) has `version_id` (composite of version and id) as `RANGE` to support `getLatestAggregateVersion()`. This can be changed to a GSI if you definitely don't need the read consistency feature. See [DynamoDB Limitations](#dynamodb) for more info.
- Event order is preserved using a generated incrementing integer id based on the current microsecond timestamp. This is necessary for compatibility with the Spatie package, see [Event Ids](#event-ids) for details.

### Snapshots

[](#snapshots)

- Snapshots are stored in a single table, but as one or more items, allowing snapshots to exceed the DynamoDB 400KB limit in size. The table has the `aggregate_uuid` as the `HASH` key and `id_part` as the `RANGE` key.
- `id_part` is a composite of a randomly generated int id and the 'part number' of the snapshot. This does two things, it means that the most recent snapshots are returned first when queried (using a DESC sort) and that the snapshot parts are returned in the correct order if the snapshot required more than one DynamoDB item to be stored.
- PHP `serialize()` is used on the output of you aggregate root's `getState()` method and the results are then base64 encoded and split into multiple parts if too large to fit inside a single DynamoDB item (400KB limit).
- When a snapshot is retrieved the parts are recombined behind the scenes to rehydrate your aggregate root.
- The total size of snapshots is not limited by DynamoDB and only constrained by the PHP memory limit of the process working with them.

### Read Consistency

[](#read-consistency)

- You can configure [consistent reads from DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html)using the `read_consistency` config key (defaults to `false`.) This only applies to methods where you pass an aggregate root UUID as an argument. Some method calls on the EventRepository such as `find($id)`and `retrieveAll(null)` will remain eventually consistent.

### Lazy Collections

[](#lazy-collections)

- The package implements a paginator that lets you iterate through large result sets as LazyCollections, backed by the AWS PHP paginator. However, bear in mind this can result in repeated DynamoDB requests, if you would rather avoid this and keep the results in memory after you access them, just call `->remember()` on the collection.

Limitations
-----------

[](#limitations)

### DynamoDB

[](#dynamodb)

- Individual Events cannot exceed 400KB in size, which is max size of a DynamoDB item.
- The maximum total size of all events data per Aggregate UUID is 10GB due to the use of a [Local Secondary Index](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html). If you do not intend to use the [read consistency](#read-consistency) feature, you can remove this limitation by moving the index `aggregate_uuid-version-id-index` to the `GlobalSecondaryIndexes` **before** creating tables.
- The package expects `PAY_PER_REQUEST` billing mode behaviour and doesn't currently support provisioned throughput. For example, there's no handling of throughput exceeded exceptions nor a wait/retry mechanism for this.

### Transactions

[](#transactions)

- The spatie package has a method on its `AggregateRoot` base class called `persistInTransaction()`, this creates a Laravel DB transaction around the storage of events. There's currently no way for the Repository to know about this transaction, so we aren't able to implement it for DynamoDB. This is not used by the package internally, so you only need to be aware if you use this method yourself.

### Event Ids

[](#event-ids)

- This package generates its own 64bit `int` Ids for events. The Spatie package interfaces expect integer ids and the logic expects them to be incrementing. DynamoDB does not provide incrementing ids.
- The Ids consist of the current microsecond timestamp expressed as an integer plus 3 random digits appended to the end. approximating the incrementing behaviour that's expected for event order and the random digits increase collision resistance in the unlikely event that two are generated in the same microsecond.
- Collisions are possible but unlikely and currently unhandled in the code, the consequences would depend on the design of your application.
- Consider also that, at scale, clock skew between servers could cause issues for this.
- You can switch to your own implementation for generation of Ids by implementing the `IdGenerator` interface and updating the config key `id_generator`.

```
    'id_generator' => TimeStampIdGenerator::class,

```

- You can switch to your own implementation of a timestamp provider for the provided `TimeStampIdGenerator` by implementing the `TimestampProvider` interface and updating the config key `id_timestamp_provider`. If you return a shorter timestamp (e.g. seconds or milliseconds) the TimeStampIdGenerator will fill the remainder of the 64bit Int with random digits.

```
    'id_timestamp_provider' => TimeStampIdGenerator::class,

```

Getting Started
---------------

[](#getting-started)

### Install

[](#install)

Install the package via composer:

```
composer require blackfrog/laravel-event-sourcing-dynamodb
```

### Configure

[](#configure)

Publish the config file with:

```
php artisan vendor:publish --tag="laravel-event-sourcing-dynamodb-config"
```

Review the config key `dynamodb-client` and make sure the appropriate ENV variables are set, or you may wish to use your own ENV variable names if the package defaults clash for you. This array is the configuration array passed to `Aws\DynamoDb\DynamoDbClient` so you can modify it to use anything the AWS package supports, including alternative authentication options. If you already use AWS, for example with DynamoDB as a Cache driver for Laravel, you should check and align your configuration for this with the one for this package to avoid confusion or duplication.

You can change the default table names using the `event-table` and `snapshot-table` config keys.

### Create DynamoDB Tables

[](#create-dynamodb-tables)

You can create the relevant DynamoDb tables with `php artisan event-sourcing-dynamodb:create-tables`. This requires appropriate AWS permissions to do so and is probably unwise to use in a production scenario. You can see (and modify at your own risk) the table specifications in `event-sourcing-dynamodb.php`. For production, we recommend you take these table specs and move them into your preferred mechanism for managing AWS resources, such as Terraform or CloudFormation.

### Update Spatie Laravel Event Sourcing Config

[](#update-spatie-laravel-event-sourcing-config)

Update the config for the Spatie Laravel Event Sourcing package in `config/event-sourcing.php` setting the value for `stored_event_repository` to `DynamoDbStoredEventRepository::class` and `snapshot_repository`to `DynamoDbSnapshotRepository::class`.

Testing
-------

[](#testing)

Running the test suite requires DynamoDBLocal, see [Local Development](#local-development) for setup.

The test suite expects this to be present and running at default ports.

**Run:**

```
composer test
```

Local Development
-----------------

[](#local-development)

For local development you can use: [DynamoDB Local](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html). There are some minor differences in behaviour from the real service, and we recommend testing against real DynamoDB in your AWS account before launching your project.

Future Roadmap Ideas
--------------------

[](#future-roadmap-ideas)

- Support for automatic event removal of events with the [DynamoDB Time To Live (TTL)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) feature, specified per event.
- More granular control over read consistency mode, e.g. configurable per method, or able to change a repo binding on the fly to get one that is read consistent.
- Support for provisioned capacity billing, specifically handling exceptions and retries.

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Credits
-------

[](#credits)

- [BlackFrog Software Consultancy](https://blackfrog.co)
- [Shaun Keating](https://github.com/shkeats)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

16

—

LowBetter than 5% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity8

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity25

Early-stage or recently created project

 Bus Factor1

Top contributor holds 92.2% 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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/bc9828dbe9f7a3c948504c2dcdfec0eea0317d9680e2c0a5e54746a9b772899e?d=identicon)[Shkeats](/maintainers/Shkeats)

---

Top Contributors

[![Shkeats](https://avatars.githubusercontent.com/u/2048674?v=4)](https://github.com/Shkeats "Shkeats (130 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (6 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (5 commits)")

### Embed Badge

![Health badge](/badges/blackfrog-laravel-event-sourcing-dynamodb/health.svg)

```
[![Health](https://phpackages.com/badges/blackfrog-laravel-event-sourcing-dynamodb/health.svg)](https://phpackages.com/packages/blackfrog-laravel-event-sourcing-dynamodb)
```

PHPackages © 2026

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