干净的架构 - 罗伯特·马丁 - 用例粒度

8
我考虑在项目中实施Robert Martin的Clean Architecture,并试图找出如何处理非平凡的用例。
我发现将架构扩展到复杂/组合用例尤其是系统作为演员而不是用户的情况(例如系统执行某种批处理)很困难。
为了说明问题,让我们假设一个像“系统更新所有帐户余额”的用例,并以伪代码实现。
class UpdateAllAccountBalancesInteraction {
    function Execute() {
        Get a list of all accounts
        For each account
            Get a list of all new transactions for account
            For each transaction
                Perform some specific calculation on the transaction
            Update account balance
    }
}

此外,“获取所有账户列表”、“获取某个账户的所有新交易记录”、“对交易执行特定计算”、“更新账户余额”都是各自有效的用例,并且每个用例已经在其自己的交互类中实现。
一些问题出现了:
  • “系统更新所有账户余额”的用例是否有效,或者应该将其拆分为更小的用例(尽管从业务角度来看似乎有意义,但它是一个合法的业务场景)?
  • UpdateAllAccountBalancesInteraction是一个合法的交互吗?
  • 一个交互是否允许/应该编排其他交互?
  • 编排其他交互的代码是否真的属于其他地方?
  • 只将UpdateAllAccountBalancesInteraction作为一个交互,而将其调用其他交互共享的函数,而不是作为其他交互的编排者,这样做是否可以接受?
2个回答

2

显然,您需要高级交互来与低级交互共享一些(或很多)常见功能。这是可以的。

如果业务需要一个名为UpdateAllAccountBalances的用例,则它是一个有效的用例,并且你命名它以反映业务逻辑是很好的。

如果这符合您的业务逻辑,那么一个交互调用其他交互也是可以的。问自己以下问题:如果UpdateAccountBalance的要求发生变化,是否也应该以完全相同的方式影响UpdateAllAccountBalances?如果答案是肯定的,那么实现这一点的最佳方法是让UpdateAllAccountBalances调用UpdateAccountBalance,否则,您需要在两个地方进行更改才能保持一致。如果答案是否定的,则要将这两个交互解耦,并通过使它们调用共享函数来实现。


我必须下定决心,继续推进我的项目,因此我得出结论:演示文稿/文章中提供的示例是琐碎的,依赖项分离是指层次结构,我认为这很容易达成共识。有了这个想法,我得出结论:一个交互的Execute()方法不应该调用另一个交互的Execute()方法,但它们可以在同一层中共享代码。 - PlusInfinite

0
我的建议是以不同的方式解决问题。使用领域模型来表示问题本身,而不是采用过程式方法。您正在看到用例存在的一些问题之一,其中一个问题是它们的粒度通常不确定。
在领域模型中,表示特定“事物”(即“帐户”)的标准方法是使用两个对象。一个表示特定帐户,另一个表示所有帐户共有的东西。
AccountCatalog (1) ---- (*) SpecificAccount

在你的例子中,SpecificAccount会有一个名为"UpdateBalance"的服务(方法)。AccountCatalog有一个名为"UpdateAllBalances"的服务(方法),它向其集合中的所有SpecificAccount发送一个名为UpdateBalance的消息。
现在任何东西都可以发送UpdateAllBalances消息。另一个对象、人类互动或另一个系统。
我应该注意到,一个账户通常会“知道”(即维护)自己的余额,而不是被告知更新。

“Clean Architecture” 方法建议采用领域模型,就像您所描述的那样。这是一个定义实体和企业业务规则的领域模型,没有依赖于其他任何东西。此外,它建议在领域模型之上实现应用程序业务规则,并将实施用例作为类,依赖于类似于 Command Design Pattern 的接口。我的问题是关于最后一层中的组合。 - PlusInfinite

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接