Replicated Event Sourcing im Überblick
Standard-Event Sourcing hat
einen Writer pro persistenceId — eine einzelne
Actor-Instanz hängt Events an; eine andere Instanz (nach Failover)
spielt sie ab. Das ist in Ordnung für Sharded Entities, wo das
Framework Ein-Actor-pro-Key garantiert.
Replicated Event Sourcing entfernt diese Einschränkung. Mehrere Replicas derselben Entity können gleichzeitig aktiv sein — auf verschiedenen Nodes, in verschiedenen Regionen — und jede persistiert unabhängig. Das Framework verwendet Vector Clocks, um nebenläufige Edits zu erkennen + Conflict Resolver, um sie zu mergen.
Wann verwenden
Abschnitt betitelt „Wann verwenden“Das ist das Nischen-Persistenz-Muster. Die meisten Apps sollten es nicht brauchen. Use Cases:
- Multi-Region Active-Active — dieselbe Entity schreibbar in EU + US. Netzwerk-Partition zwischen Regionen stoppt keine Seite.
- Edge-artige Replikation — Entities replizieren nah am User, gleichen zentral ab.
- Cluster-spanning gleichzeitige Writer — dieselbe Entity wird auf mehreren Cluster-Nodes ohne Singleton-Koordination bearbeitet.
Für typische Sharded-Entity-Setups gibt ClusterSharding + PersistentActor automatisch Exactly-One-Writer pro Key — einfacher als das hier.
Was du aufgibst
Abschnitt betitelt „Was du aufgibst“Replicated Event Sourcing tauscht Einfachheit gegen Verfügbarkeit:
| Single-Writer-ES | Replicated ES |
|---|---|
| Totale Event-Order pro pid | Partielle Order — nebenläufige Events können ungeordnet sein |
| State ist ein deterministischer Fold | State ist ein Fold + Conflict Resolution |
| Commands werden gegen den neuesten State validiert | Commands werden gegen die View der lokalen Replica validiert |
| Neustart spielt das Log ab | Neustart spielt das Log ab + gleicht nebenläufige Branches ab |
Das Mental-Model ist CRDT-artig für Events — Multi-Writer- Konvergenz by Design.
Ein minimales Beispiel
Abschnitt betitelt „Ein minimales Beispiel“import { ReplicatedEventSourcedActor, vectorClock, type ConflictResolver,} from 'actor-ts';
type State = { value: number };type Event = { kind: 'set'; value: number };
const resolver: ConflictResolver<State, Event> = { resolve(state, conflicts) { // Wenn zwei Replicas gleichzeitig unterschiedliche Werte setzen, gewinnt das Maximum: const values = conflicts.map(c => (c.event as Event).value); return { value: Math.max(...values) }; },};
class Counter extends ReplicatedEventSourcedActor<Cmd, Event, State> { readonly persistenceId = 'counter-42'; readonly replicaId = process.env.REPLICA_ID!; readonly conflictResolver = resolver;
initialState() { return { value: 0 }; } onEvent(state: State, event: Event) { return { value: event.value }; } // ... onCommand etc.}Der Actor erweitert ReplicatedEventSourcedActor statt
PersistentActor. Drei zusätzliche Dinge zu spezifizieren:
replicaId— der stabile Identifier dieser Replica (anders alspersistenceId).conflictResolver— wie nebenläufige Events gemergt werden.- Das Journal muss über Replicas geteilt sein (Cassandra, geteilter Object Storage, etc.).
Die Komponenten
Abschnitt betitelt „Die Komponenten“| Komponente | Zweck |
|---|---|
| VectorClock | Verfolgt Kausalität über Replicas — erkennt nebenläufige Writes. |
| ConflictResolver | Entscheidet, wie nebenläufige Events in einen einzigen State gemergt werden. |
| Single-Writer-Lease | Optional — gattert Writes über ein Lease für stärkere Konsistenz. |
| Replicated Snapshots | Snapshots, die die Vector Clock für volle Recovery enthalten. |
Jedes bekommt seine eigene Deep-Dive-Seite.
Wann NICHT verwenden
Abschnitt betitelt „Wann NICHT verwenden“Vergleich
Abschnitt betitelt „Vergleich“ Sharding + PersistentActor Replicated ESMehrere Writer pro Entity? Nein (genau einer) JaConflict Resolution nötig? Nein JaCross-Region Active-Active? Sharding bevorzugt eine Region JaOperative Komplexität? Niedrig HochVerwenden wenn Default Du brauchst es wirklichWie geht’s weiter
Abschnitt betitelt „Wie geht’s weiter“- Vector Clocks — wie Nebenläufigkeit erkannt wird.
- Conflict Resolver — wie nebenläufige Events mergen.
- Single-Writer-Lease — optionale Stärkere-Konsistenz-Gate.
- Replicated Snapshots — Vector-Clock-aware Snapshots.
- PersistentActor — die einfachere Single-Writer-Alternative.
- Sharding im Überblick — das übliche Scale-out-Muster.