milpa/event-store

Append-only event-log primitive for the Milpa PHP framework: durable JSONL file store and in-memory store behind a common EventStoreInterface — per-stream replay and store-wide monotonic sequencing.

Maintainers

Package info

github.com/getmilpa/event-store

pkg:composer/milpa/event-store

Transparency log

Statistics

Installs: 1 888

Dependents: 4

Suggesters: 1

Stars: 0

Open Issues: 0

v0.3.0 2026-08-19 23:39 UTC

This package is auto-updated.

Last update: 2026-08-19 23:39:38 UTC


README

Milpa

Milpa Event Store

A tiny append-only event log for the Milpa PHP framework, with zero package dependencies. Append events, replay a stream, project state from the fold. Two interchangeable stores — file (JSONL) and in-memory — behind one EventStoreInterface. The persistence primitive under Milpa's event-sourced process engine.

CI Packagist PHP License Docs

milpa/event-store is the smallest possible seam onto an append-only log: an Event is an immutable fact — streamId, type, payload, seq, recordedAt — and a store's only two jobs are "append durably" and "read a stream back in order". No ORM, no serializer, no framework coupling — construct a store with a path (or nothing at all) and call append().

recordedAt is a UTC-serialized wall-clock observation made by the process constructing a new event. It says when that process's clock observed the record, not when the domain event objectively happened, and it is no more trustworthy than that process and clock. Legacy records replay with recordedAt === null; the store never invents their time from filenames, file metadata, or payloads.

Install

composer require milpa/event-store

Quick example

use Milpa\EventStore\Event;
use Milpa\EventStore\FileEventStore;

$store = new FileEventStore('/var/data/orders.jsonl');

// Append: nextSeq() hands out the store-wide monotonic counter, one call per event.
$store->append(new Event('order-42', 'OrderPlaced', ['total' => 19.99], $store->nextSeq()));
$store->append(new Event('order-42', 'OrderShipped', ['carrier' => 'DHL'], $store->nextSeq()));

// Replay: every event for one stream, in ascending seq order — never other streams' events.
foreach ($store->replay('order-42') as $event) {
    printf("#%d %s %s\n", $event->seq, $event->type, json_encode($event->payload));
}
// #1 OrderPlaced {"total":19.99}
// #2 OrderShipped {"carrier":"DHL"}

$store->streams();  // ["order-42"]
$store->nextSeq();  // 3 — one past the highest seq in the store, across every stream

Project current state by folding the replayed events yourself — the store never stores state, only the facts it was told:

$state = array_reduce(
    $store->replay('order-42'),
    static fn (array $state, Event $event): array => match ($event->type) {
        'OrderPlaced' => [...$state, 'status' => 'placed', 'total' => $event->payload['total']],
        'OrderShipped' => [...$state, 'status' => 'shipped', 'carrier' => $event->payload['carrier']],
        default => $state,
    },
    [],
);
// ['status' => 'shipped', 'total' => 19.99, 'carrier' => 'DHL']

Two stores, one interface

Store Durability Use it for
FileEventStore Appends one JSON line per event to a flat file, under an exclusive flock() so concurrent appenders never interleave partial lines. nextSeq() and replay() both re-derive their answer from the file itself — a fresh instance pointed at the same path, in a different process or a different request, agrees with every other instance about both "what happened" and "what comes next". Real persistence — the process engine's durable log.
InMemoryEventStore An in-process array. Nothing is written to disk; nothing survives past the instance's lifetime. Tests, and zero-file consumers that don't need durability.

Both implement the same four-method EventStoreInterface (append(), replay(), nextSeq(), streams()), verified by one shared contract test suite (EventStoreContractTestCase) so behavior — sequencing, stream isolation, replay order — never drifts between the two.

Requirements

  • PHP ≥ 8.3
  • Nothing else — milpa/event-store has no package dependencies, Milpa or otherwise

Documentation

Full API reference: getmilpa.github.io/event-store — generated straight from the source DocBlocks and dressed with the Milpa design system.

Contributing

Contributions are welcome — see CONTRIBUTING.md. Please report security issues via SECURITY.md, and note that this project follows a Code of Conduct.

License

Apache-2.0 © Rodrigo Vicente - TeamX Agency.

Milpa is designed, built, and maintained by Rodrigo Vicente - TeamX Agency.