import type EventStore from "../EventStore"; import type IRepository from "../../Domain/Repository/IRepository"; import type EventSourcedAggregateRoot from "../../Domain/EventSourcedAggregateRoot"; import { Identity } from "../../Domain/AggregateRoot"; import ConcurrencyException from "../Exception/ConcurrencyException"; export type RetryCallback = (aggregate: T) => void | Promise; export default abstract class Repository implements IRepository { constructor(protected readonly eventStore: EventStore) {} public async save(aggregateRoot: T): Promise { await this.eventStore.save(aggregateRoot); } public async load(aggregateRootId: Identity): Promise { return this.eventStore.load(aggregateRootId); } public async saveWithRetry( aggregateRootId: Identity, updateFn: RetryCallback, maxRetries: number = 3 ): Promise { let lastError: Error | undefined; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const aggregate = await this.load(aggregateRootId); await updateFn(aggregate); await this.save(aggregate); return; } catch (error) { if (error instanceof ConcurrencyException && attempt < maxRetries) { lastError = error; continue; } throw error; } } throw lastError; } }