1. 从贫血模型到充血模型:半导体测试领域的DDD转型
在半导体测试设备开发领域,业务复杂度正以惊人的速度增长。一套典型的测试系统需要处理测试配方管理、晶圆批次跟踪、Die测试规则验证、机台状态控制、良率实时计算以及异常中断处理等数十个相互交织的业务场景。传统的MVC架构在这种复杂环境下很快会暴露出严重问题——业务逻辑像癌细胞一样在Service层扩散,最终形成难以维护的"上帝类"。
1.1 传统架构的痛点剖析
以测试作业中止场景为例,在传统贫血模型中,我们通常会看到这样的代码:
csharp复制public class TestJobService
{
public void AbortTestJob(long jobId)
{
var job = _repository.GetById(jobId);
// 状态校验
if (job.Status == TestJobStatus.Completed)
throw new Exception("已完成作业不能中止");
// 探针状态检查
if (_probeService.GetCurrentStage(jobId) == ProbeStage.Contacting)
throw new Exception("探针接触中禁止中止");
// 已测试Die数统计
var testedCount = _resultService.GetTestedDieCount(jobId);
if (testedCount > 0 && !_configService.AllowPartialAbort())
throw new Exception("当前配置不允许部分中止");
// 硬件指令发送
_machineClient.SendAbortCommand(job.MachineId);
// 状态更新
job.Status = TestJobStatus.Aborted;
_repository.Update(job);
// 日志记录
_logService.RecordAbortEvent(jobId, "Manual abort");
}
}
这段代码暴露了贫血模型的典型问题:
- 业务逻辑分散在多个Service中
- 状态判断与硬件操作耦合
- 修改一个规则需要跨多个文件调整
- 难以编写有意义的单元测试
1.2 DDD的领域模型重构
使用DDD的充血模型后,相同的业务逻辑被封装在领域对象内部:
csharp复制public class TestJob : AggregateRoot<long>
{
// 领域状态
public TestJobStatus Status { get; private set; }
public ProbeStage CurrentProbeStage { get; private set; }
public int TestedDieCount { get; private set; }
// 领域行为
public void Abort()
{
if (Status == TestJobStatus.Completed)
throw new BusinessException("已完成作业不能中止");
if (Status == TestJobStatus.Running &&
CurrentProbeStage == ProbeStage.Contacting)
throw new BusinessException("探针接触中禁止中止");
if (TestedDieCount > 0 && !_config.AllowPartialAbort)
throw new BusinessException("当前配置不允许部分中止");
Status = TestJobStatus.Aborted;
AddDomainEvent(new TestJobAbortedEvent(Id));
}
}
重构后的优势显而易见:
- 所有中止相关的业务规则集中在一处
- 状态变更与业务规则高度内聚
- 外部只需调用job.Abort(),无需了解内部逻辑
- 易于单元测试,可模拟各种边界条件
关键经验:在半导体测试领域,将状态机逻辑封装在领域对象内部尤为重要。测试机的中止、暂停、恢复等操作往往涉及复杂的硬件状态判断,这些规则应该由领域对象自己维护,而不是分散在多个Service中。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 半导体测试领域的DDD战术设计
2.1 聚合设计原则
在半导体测试系统中,聚合边界的划分至关重要。一个设计不当的聚合可能导致:
- 性能问题:加载过大的聚合消耗内存
- 并发冲突:过大的聚合增加锁竞争
- 业务逻辑泄露:规则被迫放在聚合外部
正确的聚合设计示例:
csharp复制// 聚合根
public class TestJob : AggregateRoot<long>
{
private readonly List<DieTestResult> _dieResults = new();
private TestRecipe _recipe;
// 核心业务方法
public void RecordDieTest(DieCoordinate coord, TestResult result)
{
// 业务规则校验
if (Status != TestJobStatus.Running)
throw new BusinessException("只有运行中的作业可以记录测试结果");
if (_dieResults.Any(r => r.DieX == coord.X && r.DieY == coord.Y))
throw new BusinessException("同一Die不能重复测试");
// 记录结果
_dieResults.Add(new DieTestResult(coord.X, coord.Y, result.Pass, result.Current));
// 触发衍生计算
RecalculateYield();
}
private void RecalculateYield()
{
Yield = (double)_d
