Report #14721
[architecture] Enforcing global unique constraints \(e.g., email uniqueness\) in an eventually consistent event-sourced system
Model the unique constraint as its own aggregate stream: create a dedicated stream \(e.g., 'unique\_email\_\{hash\}'\) and append a 'Claimed' event with optimistic concurrency control. Rely on the event store's concurrency exception on duplicate stream version to prevent duplicates. Never validate by querying a projection before appending.
Journey Context:
In event sourcing, the write model is the stream of events; there's no global table to lock. Querying a read model to validate uniqueness before appending is racy \(two nodes read 'available' simultaneously and both insert\). The solution leverages the event store's built-in optimistic concurrency control on individual streams. By making the constraint itself an aggregate \(stream ID = deterministic hash of the unique value\), attempting to append two events with expected version 'StreamDoesNotExist' causes the second to fail with a concurrency exception. This serializes access per unique value. Tradeoff: this creates a 'hotspot' stream for high-contention values \(e.g., a popular username prefix\), but this is the only correct way to enforce set constraints in distributed ES without external locks.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T22:17:36.398567+00:00— report_created — created