六边形架构DDD前端项目:Dice聚合根与Die实体重构方案问询
重构Dice聚合根与Die实体的方案
针对你遇到的核心问题(Dice实例重复创建+依赖传递繁琐、Die的roll方法暴露内部依赖),结合DDD聚合根封装原则和六边形架构的依赖倒置思想,给出以下重构方案:
1. 固化Dice聚合根的依赖注入,避免重复实例化
将IdProvider和RandomNumberProvider作为Dice聚合根的构造参数注入,而非每次调用rollDice时传递。这样Dice实例可以复用,不用每次执行逻辑都重新初始化,同时符合六边形架构的"端口-适配器"模式:依赖抽象(端口)而非具体实现,方便测试/生产环境切换。
代码实现
// 定义依赖端口(抽象接口) interface IdProvider { generate(): string; } interface RandomNumberProvider { generate(min: number, max: number): number; } // Dice聚合根 class Dice { private readonly idProvider: IdProvider; private readonly randomNumberProvider: RandomNumberProvider; private dice: Die[] = []; // 构造时注入依赖,后续方法直接使用 constructor(idProvider: IdProvider, randomNumberProvider: RandomNumberProvider) { this.idProvider = idProvider; this.randomNumberProvider = randomNumberProvider; } // 添加新骰子时,聚合根负责创建Die并注入依赖 addDie(faces: number): void { const dieId = this.idProvider.generate(); this.dice.push(new Die(dieId, faces, this.randomNumberProvider)); } // 掷所有骰子,内部直接调用Die的roll方法 rollDice(): void { this.dice.forEach(die => die.roll()); } // 暴露聚合根状态(符合DDD的状态访问规则) getState(): { id: string; faces: number; currentValue: number }[] { return this.dice.map(die => die.getState()); } }
2. 封装Die实体的roll逻辑,消除依赖泄露
将RandomNumberProvider注入Die的构造函数,让Die内部自己处理随机数生成逻辑,对外只暴露无参数的roll()方法,避免把内部依赖暴露给上层调用方,保证实体的封装性。
代码实现
class Die { private readonly id: string; private readonly faces: number; private readonly randomNumberProvider: RandomNumberProvider; private currentValue: number = 1; constructor(id: string, faces: number, randomNumberProvider: RandomNumberProvider) { this.id = id; this.faces = faces; this.randomNumberProvider = randomNumberProvider; } // 内部调用依赖生成随机数,对外无参数 roll(): void { this.currentValue = this.randomNumberProvider.generate(1, this.faces); } // 暴露实体状态 getState(): { id: string; faces: number; currentValue: number } { return { id: this.id, faces: this.faces, currentValue: this.currentValue }; } }
3. 结合Redux Toolkit与环境切换的实践
在Redux层面,通过工厂模式统一创建Dice实例,根据环境自动注入对应依赖(生产用真实实现,测试用Mock),避免在slice中重复处理依赖逻辑:
// 生产环境依赖实现 class RealIdProvider implements IdProvider { generate(): string { return crypto.randomUUID(); } } class RealRandomNumberProvider implements RandomNumberProvider { generate(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min; } } // 测试环境Mock依赖 class MockIdProvider implements IdProvider { private counter = 0; generate(): string { return `mock-die-${this.counter++}`; } } class MockRandomNumberProvider implements RandomNumberProvider { private fixedValue: number; constructor(fixedValue: number) { this.fixedValue = fixedValue; } generate(): number { return this.fixedValue; } } // Dice工厂:统一处理实例创建与依赖注入 class DiceFactory { static create(): Dice { const idProvider = process.env.NODE_ENV === 'test' ? new MockIdProvider() : new RealIdProvider(); const randomNumberProvider = process.env.NODE_ENV === 'test' ? new MockRandomNumberProvider(3) // 测试时固定返回3,方便断言 : new RealRandomNumberProvider(); return new Dice(idProvider, randomNumberProvider); } } // Redux Slice import { createSlice, PayloadAction } from '@reduxjs/toolkit'; const diceInstance = DiceFactory.create(); interface DiceState { dice: ReturnType<typeof diceInstance.getState>; } const initialState: DiceState = { dice: diceInstance.getState() }; const diceSlice = createSlice({ name: 'dice', initialState, reducers: { addDie: (state, action: PayloadAction<number>) => { diceInstance.addDie(action.payload); state.dice = diceInstance.getState(); }, rollDice: (state) => { diceInstance.rollDice(); state.dice = diceInstance.getState(); } } }); export const { addDie, rollDice } = diceSlice.actions; export default diceSlice.reducer;
4. 关键优化点总结
- 聚合根职责清晰:Dice作为聚合根,负责管理Die实体的创建与生命周期,统一处理依赖注入,符合DDD聚合根的边界规则。
- 实体封装完整:Die的roll逻辑不再暴露依赖,内部自行处理随机数生成,保证了实体的独立性。
- 环境切换便捷:通过工厂模式和依赖倒置,测试环境无需修改业务代码,直接替换Mock依赖即可,符合TDD的测试隔离需求。
内容的提问来源于stack exchange,提问作者A Mehmeto
相关产品推荐
相关产品推荐

