从 Active Record 转移到领域对象模式方法

Moving from Active Record to Domain Object pattern approach

场景如下:

我加入了一个团队,该团队正在开始开发一个相当大的金融系统,并且应该以 DDD 为目标,具有丰富的行为领域模型。但是,我可以在项目的当前区域看到许多指向 Active Record 的气味:应用程序服务中的单个 "Save" 方法;一组相当贫乏的领域对象,它们有一个大的 "Validate" 方法等。所有这些都非常像 CRUD,并且变得越来越难以管理。

我正在开发的用户故事大致翻译为 "When a Direct Debit has transitioned from particular state to another (not budgeted to budgeted), create a task item for an advisor to work on"。有多种规则决定 "not budgeted" 和 "budgeted" 的含义。

目前 DirectDebit class 根本没有 "state" 的概念,它是一个相当大的 DTO(有几个子对象 - 同样,所有 DTO)大约有 1000 行验证逻辑来​​尝试确定它应该处于什么形状。企业有 "clear" 直接借记可以处于的不同状态的概念,我相信这个领域足够丰富,可以保证一个领域模型。

DirectDebit 应用程序服务有一个 "Save" 方法,它传递更新后的直接借记(来自 UI)。该方法从存储库中获取现有的 DirectDebit,完全忽略它,在更新的直接借记对象上运行 "Validate",并将其保存到存储库中。该方法然后审计旧的和新的直接借记对象之间的变化。

我正试图摆脱这里开始发展的类似 CRUD 的怪物,并向团队展示他们如何开始重构它。

理想情况下,我想开始重构领域对象以使用状态模式,使用良好的封装,也许使用领域事件来冒泡状态已经改变的事实。应用层将有一个名为 "CreateTaskWhenBudgeted" 或类似名称的域事件处理程序来解耦逻辑。

我的问题是大型 "Save" 直接借记方法,它源自 UI。

是否有分解/重构这种方法的策略?没有简单的方法来判断 "changed" 是什么,因为没有明确的命令正在发生。或者,我应该继续添乱并忽略我认为是问题的东西....?

这是应用程序服务的简化版本:

public void SaveDirectDebit(DirectDebit directDebit)
{
    directDebit.Validate(false);

    var beforeDirectDebit = _directDebitRepository.FetchDirectDebit(directDebit.ExpenditureId.Id);

    _directDebitRepository.SaveDirectDebit(directDebit);

    var afterDirectDebit = _directDebitRepository.FetchDirectDebit(directDebit.ExpenditureId.Id);

    var accountAuditLog = CreateDirectDebitAudit(beforeDirectDebit, afterDirectDebit);

    _auditLogRepository.CreateAudit(accountAuditLog);
}

当然没有一种方法可以做到(好的方法与其他方法),你有多个参数(时间、复杂性......)。

由内而外的方式

免责声明:遗留代码越庞大,这种方法似乎越难以实现(对我来说成功的条件:你不应该是唯一关心这种重构的人)。

-> 保存到数据库是个技术活儿。你必须首先引入一些通用语言来使隐含显式,并且能够引入概念和进行设计(创建边界和分离上下文)。

反之

另一种方式是我在评论中开始描述的方式,对我来说,这与 Eric Evans 所说的有关 "bubble context" 当你被遗留系统包围时,从一个干净的小系统开始更容易有界上下文。这种隔离将帮助您应用比现有系统更好(不同)的设计。