【问题标题】:Transaction script is Antipattern?事务脚本是反模式?
【发布时间】:2013-04-14 22:21:47
【问题描述】:

关于 NoSQL 数据库的事务脚本有一个类似的主题,但这个主题是关于一般模式的。从我对事务脚本的发现来看,它根本不是面向对象的。尽管它可以在其代码的每一行中使用对象,但它基本上是程序代码。

更好的解决方案是使用域模型,结合活动记录或具有工作单元/身份映射/延迟加载/查询对象等的数据映射器。事务脚本可能很容易使用,但它实际上是过程式编程,因此应该被视为面向对象世界中的反模式。

你怎么看?你同意事务脚本是反模式吗?或者你真的有办法设计一个面向对象的事务脚本而不是变相的过程?我怀疑这是可能的。

【问题讨论】:

    标签: design-patterns data-access-layer data-access


    【解决方案1】:

    事务脚本绝对不是反模式。

    根据我对 Transaction 脚本的了解,它根本不是面向对象的。

    你是对的,确实不是。然而,这一事实并不能使它成为反模式。尽管它是一种过程方法,但实际上,它在一系列业务逻辑架构模式中仍然占有一席之地——你只需要知道在哪种情况下使用它是最佳实践——而在哪种情况下不是。简单地说:如果您的问题域非常简单,那么在您的业务逻辑中使用更复杂的模式是不值得的。

    或者 - 正如Fowler 所写:

    何时使用它

    Transaction Script 的优点在于它的简单性。以这种方式组织逻辑对于只有少量逻辑的应用程序来说是很自然的,并且它在性能或理解方面的开销非常小。

    您可能会想到的反模式称为Anemic Domain Model。当你打算并且认为你正在构建一个域模型时就是这种情况 - 因为你的问题域已经足够复杂了 - 但你实际上最终 在事务脚本中 - 因为糟糕的代码组织/弱 OO 技能。

    【讨论】:

    • 你说的完全正确,但根据我的经验,每次遇到事务脚本模式时,它都是为了弥补贫乏的域模型而创建的一团糟。称之为内疚,但当我看到这种模式时,我知道这是一个问题。
    • @HDave +1。大多数时候事务脚本不适用,恕我直言,你最好使用适当的域模型。事务脚本可行的一种情况是某种类似缓存的服务,它只存储业务逻辑在其他(微)服务中实现的实体的副本。
    【解决方案2】:

    不是反模式。事实上,大多数企业应用程序(我所见过的)都使用事务脚本,而不是富域模型模式。

    您提到的

    活动记录模式只有在您拥有相当简单的域实体到持久存储聚合(RDBMS 表)的一对一映射时才会派上用场。

    数据映射器类似于 ORM(Hibernate 和朋友)。如果您的业务逻辑驻留在域实体中,则这些实体必须自行变异。在我看来,这将改变状态(使用 ORM 时固有的)与状态本身结合起来的逻辑。从外部查看您的域模型并将您的业务逻辑放入服务(事务脚本)中会更简单。此外,如果您的业务逻辑量很大,那么当相关代码分散在域实体中时(就像将您的事务脚本混合在一起一样),就很难找到相关代码。

    但您不必最终采用完全程序化的方法,因为您可以(并且应该)将您的服务分解为自包含的高度内聚的“程序容器”。

    【讨论】:

    • 是的,我见过的大多数企业应用程序都使用事务脚本......而且在几乎所有情况下,随着复杂性的增加,它完全脱离了道路。在大多数情况下,这是因为 TS,当只是一点点 DDD 就可以解决这么多问题......因此,我讨厌 TS,因为它是一种简单的模式,但开发人员经常错过了有必要的点将业务逻辑下推到领域模型...我建议仅在非常简单的情况下使用 TS,介于 CRUD 和非常小的业务逻辑复杂性之间。
    • 百分之一百万同意@Pabzt。应用程序的复杂性增加。对我来说,在整个课程中坚持交易脚本使其成为一种反模式。我见过具有数百个服务和模型的项目,其中所有逻辑都位于服务层中。将它们全部放在一个服务文件夹中,嘿presto!
    • 我见过的大多数企业应用程序都使用事务脚本......但他们不知道,所以团队一直在谈论 DDD
    【解决方案3】:

    TS 不是 OO 或非 OO。您可以在域模型方法、服务方法或高级应用程序方法中使用它。这只是意味着您可以阅读程序的业务意图,而无需通过一百万个回调和“黑魔法”。

    这就是微软引入 async/await 的原因。它将看起来晦涩难懂的 send-a-callback(委托)和 exit,process-the-callback-in-separate-method(必需)样式转换为可读的事务脚本

    GOTO 很糟糕,因为它们破坏了事务脚本的可读流程,使其成为一个糟糕的脚本。

    a) 事务脚本出错是一种反模式。例如,一个巨大的方法,没有或很少的方法调用等,同一方法中的不同级别的操作(将它们重构为方法)。将业务流程的离散步骤集中在一个方法中(将它们分解为方法或单独的类。很多业务对象?使用 DDD 服务模式)。

    b) 不正确使用 TS 是一种反模式。例如大量的应用程序间消息传递、事件触发等,因此您无法通读并查看业务流程(技术应用程序的功能要求)。低层次的细节(技术)与功能性工作相结合。过度分离应该在一页上可见的业务活动。

    TS 的使用应该是分形的,每次放大都会深入到更详细的 TS 样式逻辑。高级:您会看到方法调用和 DDD 服务使用。中等水平可能有点混合。下层主要是域对象方法/属性调用以及最精细的逻辑细节。

    因为 TS 可能会被滥用或阻止其使用而将其扔到公共汽车下面,这只会让罐子走下坡路 - 无法分组和分离并且不知道 SRP(单一责任)/凝聚力的开发人员会搞砸其他风格,也是。答案是对他们进行业务流程培训,并举例说明分组和分离——这应该通过业务/功能需求(垂直切片)而不是技术(水平切片)来完成。

    1. 将只处理一个域对象或其类型的其他实例的逻辑放入 DO。不要从域对象(person.orders)中引用其他对象类型或将任何东西注入到域对象中。(其他 DO 或存储库等)。就这么简单,它违反了 SRP。 [方法中的低级事务脚本]
    2. 当您需要 person.orders 之类的东西,或者感觉需要注入某些东西时,请创建一个 DDD 服务(不序列化,每次使用后没有持久属性)。注入例如一个人,以及其他集合(存储库或 IQueryable 等)。在那里工作。 [此处为中级交易脚本]
    3. 在 DDD 服务的“应用程序方法”类别中组合对域对象和 DDD svc 的操作。
    4. 从程序的最高层构造和调用它们

    在每个级别,它看起来像一个 TX 脚本,不过要遵守规则。保持方法小。到时候你就可以阅读了!

    注意:在另一个答案中提供的链接中,Fowler 告诉您如何使交易脚本正确与错误:

    https://www.informit.com/articles/article.aspx?p=1398617

    他也确实暗示这不是 OO。我认为您可以将它与 OO 混合并使用 TS 专业人士(可读性和一百个专业人士),以及数百名 OO 专业人士。也就是说,您可以将TS元素放在一个领域模型中,并在更高层次的TS中组合领域模型的使用。

    还将事务脚本定义为单个数据库事务。由于您的域模型不应该注入存储库(将域对象注入存储库),因此您实际上可以像这样组织它,调用相关存储库以在最高级别(取消)持久化。但如果不是这样,关键是要有一个没有过度分离的可读代码流。

    抨击 TS 的问题在于它使人们认为 SRP 完全是关于 SoC(关注点分离),而他们永远不必担心内聚(将相同的东西放在一起,这也意味着 SoC,但需要组织)。因此,好心的工程师只是将事物分成一百万个部分(因为越多越好),并且更难辨别逻辑。

    【讨论】:

    • 您的建议是纯 TS 和过程编程。您一直提到域对象和 DDD,但这完全是误导。 DDD 的核心思想之一是围绕聚合进行设计,聚合是具有丰富行为的领域对象图,在实现业务逻辑的同时保护业务不变量。你的建议完全违背了这一点。提倡 TS 是可以的。当您谈论的只是一堆在贫乏领域模型上运行的程序时,通过谈论 DO 或 DDD 来误导读者是不对的。
    • SRP & Cohesion > 贫血域反模式。如果一个域对象“知道”另一个域对象(我的意思是可序列化/业务实体,而不是聚合多个 DO 的服务对象),则会削弱内聚力(以及其他不好的事情发生,比如现在你必须模拟测试等)。我知道整个行业都在做person.orders,我和你一样看到了它的吸引力。但是在70年代,他们都做了GOTO。如果你使用结构,那是贫血的。如果 Anemic 意味着违反 SRP,那么它必须作为反模式消失(或重新定义)。 DDD 也有 20 年历史了,它可以进化....
    • 分层数据库也曾经是必不可少的,并且被认为也很方便。这是 D.O. 的类比。像 person.orders 这样的相互引用。从表面上看,对关系进行硬编码似乎更好。而且它的性能更高。但最终这并不是成本更高的地方,而且这是一场失败的游戏。
    猜你喜欢
    • 2012-12-04
    • 1970-01-01
    • 2011-03-03
    • 1970-01-01
    • 2019-05-23
    • 1970-01-01
    • 2019-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多