IOperatorEventStore.cs
1 using GUNRPG.Core.Operators; 2 3 namespace GUNRPG.Application.Operators; 4 5 /// <summary> 6 /// Abstraction for storing and retrieving operator events. 7 /// Events are append-only and must maintain hash chain integrity. 8 /// Implementations must ensure atomic writes and proper ordering. 9 /// </summary> 10 public interface IOperatorEventStore 11 { 12 /// <summary> 13 /// Appends a new event to an operator's event stream. 14 /// Events must be appended in sequence order with no gaps. 15 /// </summary> 16 /// <param name="evt">The event to append</param> 17 /// <exception cref="InvalidOperationException">If sequence number is invalid or hash chain is broken</exception> 18 Task AppendEventAsync(OperatorEvent evt); 19 20 /// <summary> 21 /// Appends multiple events atomically to an operator's event stream. 22 /// All events must belong to the same operator and be in sequence order. 23 /// Either all events are appended or none are (transactional). 24 /// </summary> 25 /// <param name="events">The events to append in order</param> 26 /// <exception cref="InvalidOperationException">If events are invalid, out of order, or from different operators</exception> 27 Task AppendEventsAsync(IReadOnlyList<OperatorEvent> events); 28 29 /// <summary> 30 /// Loads all events for a specific operator, ordered by sequence number. 31 /// Returns empty list if operator has no events. 32 /// Verifies hash chain integrity during load - fails fast if tampering detected. 33 /// </summary> 34 /// <param name="operatorId">The operator to load events for</param> 35 /// <returns>Ordered list of events</returns> 36 /// <exception cref="InvalidOperationException">If hash chain verification fails</exception> 37 Task<IReadOnlyList<OperatorEvent>> LoadEventsAsync(OperatorId operatorId); 38 39 /// <summary> 40 /// Checks if an operator exists (has at least one event). 41 /// </summary> 42 Task<bool> OperatorExistsAsync(OperatorId operatorId); 43 44 /// <summary> 45 /// Gets the current sequence number for an operator. 46 /// Returns -1 if operator doesn't exist. 47 /// </summary> 48 Task<long> GetCurrentSequenceAsync(OperatorId operatorId); 49 50 /// <summary> 51 /// Lists all known operator IDs that have events. 52 /// For production use, consider adding pagination. 53 /// </summary> 54 Task<IReadOnlyList<OperatorId>> ListOperatorIdsAsync(); 55 56 /// <summary> 57 /// Lists operator IDs that belong to a specific account. 58 /// Only returns operators whose genesis event is associated with the given account. 59 /// </summary> 60 Task<IReadOnlyList<OperatorId>> ListOperatorIdsByAccountAsync(Guid accountId); 61 62 /// <summary> 63 /// Returns the account ID associated with an operator, or null if no account is set. 64 /// </summary> 65 Task<Guid?> GetOperatorAccountIdAsync(OperatorId operatorId); 66 67 /// <summary> 68 /// Associates an operator with an account by setting the AccountId on the genesis event document. 69 /// This must be called once after the operator is created. 70 /// </summary> 71 Task AssociateOperatorWithAccountAsync(OperatorId operatorId, Guid accountId); 72 }