MongoDB configuration
Transactions and atomic batches
The adapter probes transaction support lazily on the first write. With the
default configuration, a deployment without transactions refuses to write
instead of silently weakening EventStorageInterface::storeEvents().
If a standalone deployment is an explicit operational decision, opt into the best-effort compensating path:
use DomainFlow\EventSourcingMongoDB\Storage\MongoDbEventStorage;
$storage = new MongoDbEventStorage(
$database,
allowNonAtomicBatches: true,
);
This path validates versions, inserts in order, and removes documents that landed when a later insert fails. A process dying before compensation completes can still leave a partial batch. The normal replica-set path has the stronger transactional guarantee.
The adapter uses w: majority with journaling for event and counter writes.
This is fixed by the adapter so an acknowledged event is not accepted with a
weaker durability setting by accident.
BSON event documents
Events are stored one per document in the events collection. The payload and
metadata are native BSON documents, not JSON strings. The standard fields are
event_id, aggregate_id, event_class, version, occurred_on,
global_position, payload, and metadata.
Use MongoDbEventEntryFactory when you need a custom event factory or an
EventTypeRegistry:
use DomainFlow\EventSourcing\Event\EventTypeRegistry;
use DomainFlow\EventSourcingMongoDB\Storage\MongoDbEventEntryFactory;
$types = EventTypeRegistry::fromClasses([OrderPlaced::class]);
$entryFactory = new MongoDbEventEntryFactory(
eventTypes: $types,
);
$storage = new MongoDbEventStorage($database, $entryFactory);
An event with getDatabaseFields() can add top-level document fields. Those
fields are adapter data, not part of the event payload.
Indexes and ordering
The schema manager creates a unique (aggregate_id, version) index for event
concurrency and a unique global_position index for global reads. Events in an
aggregate are read by version. Global reads use a monotonic counter and are
read through CatchUpReader when concurrent writers must be tolerated.
If custom collection names are used in storage constructors, create equivalent
indexes for those collections yourself; MongoDbSchemaManager describes the
default collection names.
Snapshots and process managers
MongoDbSnapshotStorage, MongoDbSnapshotHistoryStorage, and
MongoDbProcessManagerStorage store state as native BSON documents. Process
manager writes use the loaded state version as an optimistic concurrency check.
Timeout lookup is supported by an index on timeout.
Transactional outbox
The MongoDB outbox must use the same database as the event storage:
use DomainFlow\EventSourcing\Facade\EventSourcingFacade;
use DomainFlow\EventSourcing\Outbox\OutboxRelay;
use DomainFlow\EventSourcingMongoDB\Outbox\MongoDbOutboxStorage;
use DomainFlow\EventSourcingMongoDB\Storage\MongoDbEventStorage;
$outbox = new MongoDbOutboxStorage($database);
$storage = new MongoDbEventStorage($database, outbox: $outbox);
// Do not pass an inline dispatcher to this facade. The relay is the delivery path.
$facade = new EventSourcingFacade($storage);
$relay = new OutboxRelay($outbox, $dispatcher);
The outbox entry is enrolled in the same MongoDB session as the event transaction. Delivery remains at least once and consumers must be idempotent.