You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

六边形架构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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 18:40:32