干净的架构——在哪里放置输入验证逻辑? [关闭]

Posted

技术标签:

【中文标题】干净的架构——在哪里放置输入验证逻辑? [关闭]【英文标题】:Clean architecture - where to put input validation logic? [closed] 【发布时间】:2019-12-27 10:17:40 【问题描述】:

也许在应用程序中我有一个功能,允许用户使用带有一些验证逻辑的表单发送反馈:

名称可以为空 反馈信息至少应包含 5 个字符

您会将这些验证逻辑放在哪里,要么作为业务逻辑放在 domain layer 中,要么作为 UI 逻辑放在 presentation layer 中?

这些逻辑适用于所有应用程序(androidios、web)。请注意,我们已经进行了服务器端验证。

【问题讨论】:

【参考方案1】:

我猜这个@sufian 引用的example of Uncle Bob 和这个article 在做出这个决定时会很有用。

Naoto 指出,正如 Clean Architecture 按层划分职责一样,每一层都有自己的验证逻辑

在每一层中,系统应该拒绝违反其层职责的输入。因此验证的含义根据其上下文而有所不同。

在应用层中,作为验证,我们必须确保域对象可以接收输入。我们应该拒绝域对象无法接收的输入。

例如,当缺少某些强制参数时,应该拒绝它,因为域对象无法接收这样的参数。

【讨论】:

【参考方案2】:

我认为许多开发人员在Presentation 层中这样做,特别是在ViewModel/Presenter/Controller 中(不是Activity/Fragment/View! 中)。我的方法是将这个逻辑放在Domain 层中。为什么?

是表示逻辑还是域逻辑?表示逻辑是您决定“映射渲染模型”、“渲染模型的格式”、“如何渲染”、“什么颜色、什么大小、什么文本”、“它会在屏幕上停留多长时间”等...如果验证是表示逻辑,为什么后端代码具有相同的验证控制?在我看来,验证是领域逻辑。 为什么验证是域逻辑?谁决定用户名是否最多可以是 20 个字符?业务规则决定。谁决定购物篮中的最大商品数量?业务规则决定。用户名的长度由业务决定,该规则适用于项目中的任何地方。 CreateProfile/UpdateProfile/Register 等都具有相同的 max-20char-username 规则。 长度控制(验证)代码应该驻留在域层中。 如果验证码在域层,流程是什么?用户单击视图中的按钮。 ViewModel/Presenter 调用领域层函数。域层函数验证输入数据。如果输入参数无效,则返回ValidationException 并说明。 ValidationException 将包含无效参数列表验证类型它们失败(minLength、maxLength、emailPatternMismatch 等),这是预期的(最多 20 个字符等。)。 ViewModel/Presenter/Controller 得到这个 ValidationException ,这里我们有 演示逻辑。现在它决定渲染什么,如何渲染。我们是渲染所有无效输入的错误还是只渲染第一个无效输入?应该显示什么文本/颜色(基于 ValidationException 中的数据)?我们是否将错误呈现为弹出/文本视图/工具提示?在做出所有演示决定并创建新模型之后,View 就可以了!使用该模型进行渲染。 还有一点是,在域层,验证码应该放在哪里?在 UseCase 函数或模型(为什么不)本身中?恕我直言,应该有具有通用验证逻辑的无状态通用接口/类。在那之后,每个 UseCase 类都可以实现 ValidationInterface 或将其作为 Class 对象注入。如果多个 UseCases 需要相同的验证,验证控制逻辑将被复​​制。如果我们将验证逻辑放入模型本身会发生什么?模型将实现 ValidationInterface(仅具有无状态纯函数!)并具有 fun validate():ValidationOutcome 函数。我不认为将业务模型的验证逻辑放在自身中是有问题的。所有用例都只会调用model.validate()。 Model 和 ValidationOutcome 之间存在依赖关系。

【讨论】:

有见地。 Bob 叔叔在one of the threads 中也说过类似的话。 :) 很好解释。 ValidationException 让我们明白了这一点。 来自 Bob 叔叔的评论 @Sufian 提到,在 Clean Arch 的上下文中,每一层都可以/应该有自己的验证。所以我认为将验证放在域层中会验证域而不是演示者或实体。例如,客户评论长度不应超过 100 个字符,但管理员最多可以回答 1000 个字符。就像在干净的架构中一样,我们不应该改变内层,通过改变外层,似乎这里我们需要在表示层中进行验证。【参考方案3】:

我不是 android 也不是 ios 开发人员,但我在 web 开发方面有一些经验。一些同事经常问这个问题。对我来说,答案是两者兼而有之。

例如,如果您在表示层中有验证逻辑,则每当用户发送错误的输入时,您必须转到服务器,验证然后返回错误。为了避免询问服务器,您可以使用 html5 或 javascript 验证表示层。如果某些输入是错误的,则会向用户显示,并且与服务器没有通信(因此您避免了一个请求)。但是这个验证可以很容易地跳过,所以如果用户改变了一些东西或者用一个工具(比如邮递员)做请求,这个验证就不会发生。因此,您无法确定您收到的数据是否正常。为此,您也需要服务器验证。

对我来说,这是更安全的解决方案,您只使用 UI 来避免对服务器的错误请求。

希望这会有所帮助。

【讨论】:

同意我们应该始终进行服务器端验证,但我真正关心的是客户端的验证逻辑,以避免冗余请求并获得更好的用户体验,用户无需等待 api 验证,例如,它也会在打字时立即做出反应。 是的,我理解你。对我来说,一个 api 应该总是有两种类型。带有验证(不涉及服务器)和服务器验证的 UX。因为您不应该 100% 信任前端或 UX 发送到您的服务器的数据。所以答案是将验证登录放在服务器和用户体验中。

以上是关于干净的架构——在哪里放置输入验证逻辑? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在 DDD 架构中,我应该将与按角色用户过滤数据相关的查询逻辑放在哪里

我必须在哪里包含我的应用程序的业务逻辑? [关闭]

如果涉及访问外部API(域驱动设计),我应该在哪里放置值对象创建逻辑]] << [

在哪里放置视图逻辑?

ASP.NET MVC 2 在哪里放置逻辑

在哪里放置业务逻辑,在控制器或服务中? [复制]