【问题标题】:In which layer should the validations be done mainly in the context of DDD?主要在 DDD 的上下文中应该在哪一层进行验证?
【发布时间】:2019-03-02 05:05:53
【问题描述】:

这个问题可能会被问一千次,但答案中有很多混乱和矛盾。

我在域驱动设计的背景下询问验证

  1. 应该主要在哪一层进行验证?
  2. 对象处于无效状态是否可以接受?因为很多回答说没关系,主要是因为历史数据,业务规则可能会随着时间的推移而变化,加载历史数据可能会导致问题?
  3. 尽管 Martin Fowler 建议 Replacing Throwing Exceptions with Notification in Validations,但许多实现都考虑在域层中抛出异常并将消息映射到 UI!何时返回消息以及何时在验证上下文中抛出异常?
  4. 许多文章在他们的文章中解释了一些提示或路径,例如 Vladimir Khorikovjbogard,但在 cmets 中,他们承认他们现在做事略有不同。这些模式仍然有效吗?

  5. 我应该使用像FluentValidation 这样的框架吗?如果我使用它,这个框架是否仅在应用程序层中使用,作为MVC annotations 的替代品?

  6. 什么时候应该改用Business Rule Framework (BRF)

我在这里知道很多问题,但它们针对的是同一个问题(DDD 中的验证)。

注意:我不使用CQRS 模式,因为它使应用程序变得如此复杂。 所以我有(domain layer,data layer, application layer(MVC), and shared kernel)

【问题讨论】:

  • 也许question & answer 会有所帮助。
  • @plalx 这是一个非常棒的答案,但它回答了我的部分问题,如果你能帮助我回答我的问题,我将不胜感激。

标签: asp.net-mvc validation design-patterns domain-driven-design enterprise


【解决方案1】:

实际上有几种不同的活动可以称为“验证”,它们的处理方式都略有不同。

消息验证通常发生在尽可能接近边界的地方。当我收到一个 HTTP 请求时,我将验证请求本身是否格式正确,是否在元数据中指定了正确的媒体类型,是否可以干净地处理请求正文,生成的 DOM 是否包含所有必填字段,已知数据节点都是适当的类型,存在的值在允许的范围内,所有这些都在我担心域模型的当前状态之前。

这种验证通常采用将消息中的数据转换为域中值对象图的形式;这通常看起来像工厂或构建器,它们知道如何采用与域无关的值类型并将它们转换为特定于域的值。域模型通常不知道消息格式,也不知道序列化(JSON 通常不是域关注的问题)。

从持久存储读取值时,存在类似的关注点分离 - 值工厂将知道如何从原语创建值,但不一定知道有关 JSON 或结果集等的任何信息。

验证给定消息是否有意义的“业务逻辑”考虑到域的当前状态,通常存在于域模型中。

对象处于无效状态是否可以接受?

对象处于无效状态是绝对不能接受的。

但是有一些有效的状态不是可达。负账户余额正成为公司的一项重大负债,因此引入了一项新的业务规则,以防止可能导致负余额的提款。这不会改变 Bob 的账户余额为负的事实。它仍然是一个有效的状态,只是新规则无法达到的状态。

什么时候返回消息,什么时候在验证上下文中抛出异常?

不要使用异常来实施应急管理。

【讨论】:

  • 非常感谢,但我主要关心的是域模型层中的业务规则验证,而不是琐碎的输入验证,请您帮我回答其余问题
【解决方案2】:

验证应该主要在哪一层进行?

主要在域中,除了基础设施相关的验证,例如例如 xsd 验证或 json 架构。

对象处于无效状态是否可以接受?因为很多 答案说没关系,主要是因为历史数据和 业务规则可能会随着时间和加载历史数据而改变 可能会导致问题?

这是可以接受的,因为验证是在域中完成的,因此不应如此。从业务的角度来看,对象不能处于无效的业务状态,但是,有时,就像在现实生活中一样,流程可能处于无效/临时状态。我们称之为最终一致性(https://en.wikipedia.org/wiki/Eventual_consistency),我建议你看看这个。最后系统将处于有效状态,仅此而已,如果它暂时无效,那么维护这样的系统可能会付出更大的努力,但有时您别无选择。

许多实现都考虑在领域层抛出异常 并将消息映射到 UI,尽管 Martin Fowler 建议 在验证中用通知替换抛出异常! 验证中何时返回消息以及何时抛出异常 上下文?

我不是领域层异常的忠实拥护者,除非这显然是一个先决条件论文被打破。例如,输入对于某个字段来说太大,或者某个项目的负价格。如果您无法构建有效的业务对象,那么在我看来,这是一个非常有效的例外情况。如果这是一个商业案例,则最适合使用消息。

许多文章解释了一些提示或路径,例如弗拉基米尔 Khorikov 和 jbogard 在他们的文章中,但在他们的 cmets 承认他们现在做事有点不同。这些图案是 还有效吗?

我应该使用像 FluentValidation 这样的框架吗?如果我使用它,这是 仅在应用程序层中使用的框架作为替代 MVC 注释?

DDD 中的最佳建议是永远不要使用框架,但 Spring 或 JDBC 可能会有所帮助,但通常您应该手动完成。我们甚至手工编写了存储、应用程序服务和 Oracle 预测和事件总线。它更快,更易于维护,并且您会学到更多。 Vaughn Vernon 在他的书(实施领域驱动设计)中提供了非常好的示例和一个您可以查看的项目:https://github.com/VaughnVernon/IDDD_Samples(用 Java 编写)

什么时候应该改用业务规则框架 (BRF)?

再次强调,不要使用框架

【讨论】:

  • 请问是否不建议在应用层使用FluentValidation framework?因为我们已经使用了MVC annotations
  • 我只会在表示层(例如 Angular)中使用框架,而且我倾向于不再走这条路......
  • 请问不使用任何框架的建议是否也适用于像 post c# 这样的 AOP?
  • AOP 在某些情况下很不错,例如事务支持或更多技术方面,例如在调用应用程序服务方法后刷新消息日志。 Vaughn Vernon 有一些例子(用 Java 编写,你可能会看):github.com/VaughnVernon/IDDD_Samples/blob/master/…(事务性)github.com/VaughnVernon/IDDD_Samples/blob/master/…(方面)
【解决方案3】:

您的问题有直接的答案。所以我把答案放在没有背景的地方。

验证应该主要在哪一层进行?

服务器端和客户端都提供更准确和安全的应用程序。无论设计环境如何。对于服务器端,您可以采用不同的方式,如流式验证或数据注释(模型),或者使用 jquery-unobtrusive-ajax 等集成库将它们带到客户端。 服务器端验证更重要,因为需要验证 CRUD 操作以避免异常等...... 就您的问题而言,层是视图和模型(数据访问)。

对象处于无效状态是否可以接受?因为很多 答案说没关系,主要是因为历史数据和 业务规则可能会随着时间和加载历史数据而改变 可能会导致问题?

当您在数据库中显示或处理数据存储时,所需依赖项的必填字段或空值会引发错误,这是可以接受的。在这里,没有谈论随着时间的推移可能发生的变化。我们现在只考虑。我们采用模式和编程规则来创造灵活性/可维护性。验证和条目依赖关系可以随着时间而改变。

许多实现都考虑在领域层抛出异常 并将消息映射到 UI,尽管 Martin Fowler 建议 在验证中用通知替换抛出异常! 验证中何时返回消息以及何时抛出异常 上下文?

在客户端显示异常是开发日或通知相应用户有关阻止数据更改/存储的错误的好方法。 考虑到这一点:有些系统确实没有向最终用户显示附加信息的策略。一些报告可能会使应用程序更容易受到入侵。这完全取决于您正在开发的软件类型。一个好的做法是在客户端显示一个简单的错误并将错误日志存储在服务器内(包含全面的详细信息)。

许多文章解释了一些提示或路径,例如弗拉基米尔 Khorikov 和 jbogard 在他们的文章中,但在他们的 cmets 承认他们现在做事有点不同。这些图案是 还有效吗?

有些人可能有自己命名的个人架构。但是其中一些是官方的并被广泛使用,例如 Unit Of WorkRepository Pattern,它们在著名模式 (MVC) 中添加了一些层,以实现更准确、干净和可维护的代码/应用。遵循任何模式背后的主要目的。

我应该使用像 FluentValidation 这样的框架吗?如果我使用它,这是 仅在应用程序层中使用的框架作为替代 MVC 注释?何时应该使用业务规则框架 (BRF) 代替?

FluentValidationDataAnnotations 的替代品,其工作方式类似于 FluentAPI。请注意,两者都用于为属于已定义类(数据库表)的属性定义规则。有一个名为 ViewModel 的概念,它包含主要针对前端验证的主模型类(表)的转换(有一些变化)。您可以在一个项目中同时使用这两种方法,将每个模型映射到它的视图模型,反之亦然。如果您使用的是存储库模式,比如说,有一个数据访问层,那么一些验证就在这个层内。如果您使用的是 ViewModel,那么它在应用程序层内部。但是,作为建议,这些毫无价值。成功的关键在于了解任何技术/架构/模式背后的主要目的。您可以找到大量关于它们的文章并专注于目标,然后您可以决定如何获得更干净/标准/可维护/灵活/等等...的代码。

最终提示:增加模块化会增加集成成本(软件成本),尽管会降低每个模块的成本。为您的项目使用适度的设计。组合架构有时不仅不是一个好主意,而且会增加成本和开发难度。更多详情software design basics

【讨论】:

    猜你喜欢
    • 2013-04-29
    • 1970-01-01
    • 2023-03-21
    • 2015-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多