聚合根类中的Version属性是否泄露事件溯源实现细节?
这确实属于基础设施细节泄露,解决方案看这里
你观察得很准——version属性完全是事件溯源存储和并发控制的基础设施需求,和领域核心逻辑没有关系,把它放在领域层的聚合根基类里,确实违反了清洁架构中“领域层独立于基础设施”的原则,属于细节泄露。
下面是几个可行的解决思路:
1. 将版本信息与聚合根分离,由基础设施层维护
在基础设施层(比如事件存储仓库)中单独维护聚合根的版本映射,领域层的聚合根完全不知道version的存在。举个JS的例子:
// 领域层 - 纯领域聚合根 abstract class Aggregate { public ID: string; // 只包含领域相关的方法和属性 applyEvent(event: DomainEvent): void { // 领域逻辑处理事件 } } // 基础设施层 - 事件存储仓库 class EventStoreRepository { private aggregateVersions: Map<string, string> = new Map(); async save(aggregate: Aggregate, newEvents: DomainEvent[]): Promise<void> { const currentVersion = this.aggregateVersions.get(aggregate.ID) || "0"; // 基于currentVersion做并发校验(比如乐观锁) // 保存事件到存储 // 更新版本为新的版本号 this.aggregateVersions.set(aggregate.ID, calculateNewVersion(currentVersion)); } async load<T extends Aggregate>(aggregateType: new () => T, id: string): Promise<T> { const aggregate = new aggregateType(); aggregate.ID = id; const events = await this.loadEvents(id); // 应用事件重建聚合根状态 events.forEach(event => aggregate.applyEvent(event)); // 加载对应的版本,但不传递给聚合根本身 this.aggregateVersions.set(id, getLastEventVersion(events)); return aggregate; } }
2. 用领域事件的元数据承载版本信息
把版本信息作为每个领域事件的元数据(而非聚合根的属性),当加载聚合根时,通过事件序列的元数据计算当前版本;保存时,基于最后一个事件的版本做并发控制。这样聚合根全程不需要接触版本属性:
// 领域层 - 纯领域事件,不带版本 interface DomainEvent { aggregateId: string; eventType: string; // 领域事件数据 } // 基础设施层 - 带元数据的存储事件 interface StoredEvent extends DomainEvent { version: string; timestamp: Date; } // 基础设施层 - 仓库实现 class EventStoreRepository { async save(aggregate: Aggregate, newEvents: DomainEvent[]): Promise<void> { const lastEvent = await this.getLastEvent(aggregate.ID); const nextVersion = lastEvent ? incrementVersion(lastEvent.version) : "1"; // 给新事件添加版本元数据,然后保存 const storedEvents = newEvents.map((event, index) => ({ ...event, version: incrementVersion(nextVersion, index) })); // 保存到存储,并做并发校验 } }
3. 用接口分离隔离版本属性
在领域层定义只包含领域逻辑的聚合根接口,基础设施层实现该接口时再加入版本属性。领域层依赖接口而非具体实现,从而隔离基础设施细节:
// 领域层 - 聚合根接口 interface DomainAggregate { ID: string; applyEvent(event: DomainEvent): void; // 其他领域方法 } // 领域层 - 抽象实现(可选) abstract class BaseAggregate implements DomainAggregate { public ID: string; applyEvent(event: DomainEvent): void { // 通用事件处理逻辑 } } // 基础设施层 - 带版本的具体实现 class EventSourcedAggregate extends BaseAggregate { public version: string; // 仅在基础设施层使用 // 实现领域方法 }
很多示例把version放在聚合根基类里,只是为了快速实现,忽略了架构边界。如果要严格遵循清洁架构,必须把这类基础设施细节从领域层剥离出去,保持领域层的纯粹性。
内容的提问来源于stack exchange,提问作者alaboudi
相关产品推荐
相关产品推荐

