干净的架构 - 在哪里放置输入验证逻辑?
Clean architecture - where to put input validation logic?
也许在应用程序中我有一个功能允许用户使用带有一些验证逻辑的表单发送反馈:
- 名称可以为空
- 反馈消息应至少包含 5 个字符
您会将这些验证逻辑放在哪里,是作为业务逻辑放在 domain layer
中,还是作为 UI 逻辑放在 presentation layer
中?
这些逻辑适用于所有应用程序(android、iOS、web)。请注意,我们已经进行了服务器端验证。
我不是 android 也不是 ios 开发人员,但我在网络开发方面有一些经验。这个问题经常被一些同事问到。对我来说,答案都是。
例如,如果您在表示层中有验证逻辑,每当用户发送错误输入时,您必须转到服务器,验证然后 return 错误。为避免询问服务器,您可以使用 html5 或 javascript 验证表示层。如果某些输入错误,则会向用户显示,并且不会与服务器通信(因此您避免了一个请求)。但是可以轻松跳过此验证,因此如果用户更改某些内容或使用工具(如邮递员)发出请求,则不会发生此验证。所以,你不能确定你收到的数据是好的。为此,您还需要服务器验证。
对我来说,这是更安全的解决方案,您只需使用 UI 来避免对服务器的错误请求。
希望对您有所帮助。
我认为许多开发人员在 Presentation
层中这样做,特别是在 ViewModel/Presenter/Controller
中( 而不是 在 Activity/Fragment/View!
中)。我的方法是将该逻辑放在 Domain
层中。为什么?
- 是表现逻辑还是领域逻辑?表示逻辑由您决定 "mapping render model"、"format of render model"、"how to render"、"what color, what size, which text"、"how long will it stay on screen" 等...如果验证是表示逻辑,为什么后端代码有相同的验证控制?在我看来,验证是域逻辑。
- 为什么验证是域逻辑?谁决定用户名是否最多可以是 20 个字符?业务规则决定。谁决定购物篮中的最大商品数量?业务规则决定。用户名的长度由业务决定,该规则适用于项目中的处处。 CreateProfile/ UpdateProfile/ Register 等。都有相同的 max-20char-username 规则。 长度控制(验证)代码应该驻留在域层。
- 如果验证码在领域层,流程是怎样的?用户单击视图中的按钮。 ViewModel/Presenter 调用域层函数。域层函数验证输入数据。如果输入参数无效,则returns
ValidationException
并附上说明。 ValidationException
将包含 无效参数列表 、 验证类型 它们失败了 (minLength、maxLength、emailPatternMismatch 等。 .),这是预期的( 最多 20 个字符等..)。 ViewModel/Presenter/Controller
得到这个 ValidationException
并且这里我们有 表示逻辑。现在它决定渲染什么,如何渲染。我们是渲染所有无效输入的错误还是只渲染第一个无效输入? text/color 应该显示什么(基于 ValidationException 中的数据)?我们是否将错误渲染为 popup/textView/tooltip?在做出所有演示决策并创建新模型后,View
就这样!使用该模型渲染。
- 还有一点,在Domain层,验证码应该放在哪里?在用例函数或模型(为什么不)本身?恕我直言,应该有具有通用验证逻辑的无状态通用 Interface/Class。在那之后,每个用例 class 都可以实现 ValidationInterface 或将其作为 Class 对象注入。如果多个用例需要相同的验证,验证控制逻辑将被复制。如果我们将验证逻辑放在模型本身中会发生什么?模型将实现 ValidationInterface( 仅具有无状态纯函数!)并具有
fun validate():ValidationOutcome
函数。我认为将业务模型的验证逻辑放在自己身上不是问题。所有用例只会调用 model.validate()
。 Model 和 ValidationOutcome 之间存在依赖关系。
我想这个 example of Uncle Bob quoted by @sufian and this article 在做出那个决定时会很有用。
Naoto 指出,正如 Clean Architecture 按层划分责任一样,每一层都有自己的验证逻辑。
在每一层中,系统应该拒绝违反其层责任的输入。因此验证的含义根据其上下文而有所不同。
在应用层,作为验证,我们必须确保领域对象可以接收输入。我们应该拒绝域对象无法接收的输入。
比如缺少一些强制参数时,应该拒绝,因为领域对象没有办法接收像那个参数。
也许在应用程序中我有一个功能允许用户使用带有一些验证逻辑的表单发送反馈:
- 名称可以为空
- 反馈消息应至少包含 5 个字符
您会将这些验证逻辑放在哪里,是作为业务逻辑放在 domain layer
中,还是作为 UI 逻辑放在 presentation layer
中?
这些逻辑适用于所有应用程序(android、iOS、web)。请注意,我们已经进行了服务器端验证。
我不是 android 也不是 ios 开发人员,但我在网络开发方面有一些经验。这个问题经常被一些同事问到。对我来说,答案都是。
例如,如果您在表示层中有验证逻辑,每当用户发送错误输入时,您必须转到服务器,验证然后 return 错误。为避免询问服务器,您可以使用 html5 或 javascript 验证表示层。如果某些输入错误,则会向用户显示,并且不会与服务器通信(因此您避免了一个请求)。但是可以轻松跳过此验证,因此如果用户更改某些内容或使用工具(如邮递员)发出请求,则不会发生此验证。所以,你不能确定你收到的数据是好的。为此,您还需要服务器验证。
对我来说,这是更安全的解决方案,您只需使用 UI 来避免对服务器的错误请求。
希望对您有所帮助。
我认为许多开发人员在 Presentation
层中这样做,特别是在 ViewModel/Presenter/Controller
中( 而不是 在 Activity/Fragment/View!
中)。我的方法是将该逻辑放在 Domain
层中。为什么?
- 是表现逻辑还是领域逻辑?表示逻辑由您决定 "mapping render model"、"format of render model"、"how to render"、"what color, what size, which text"、"how long will it stay on screen" 等...如果验证是表示逻辑,为什么后端代码有相同的验证控制?在我看来,验证是域逻辑。
- 为什么验证是域逻辑?谁决定用户名是否最多可以是 20 个字符?业务规则决定。谁决定购物篮中的最大商品数量?业务规则决定。用户名的长度由业务决定,该规则适用于项目中的处处。 CreateProfile/ UpdateProfile/ Register 等。都有相同的 max-20char-username 规则。 长度控制(验证)代码应该驻留在域层。
- 如果验证码在领域层,流程是怎样的?用户单击视图中的按钮。 ViewModel/Presenter 调用域层函数。域层函数验证输入数据。如果输入参数无效,则returns
ValidationException
并附上说明。ValidationException
将包含 无效参数列表 、 验证类型 它们失败了 (minLength、maxLength、emailPatternMismatch 等。 .),这是预期的( 最多 20 个字符等..)。ViewModel/Presenter/Controller
得到这个ValidationException
并且这里我们有 表示逻辑。现在它决定渲染什么,如何渲染。我们是渲染所有无效输入的错误还是只渲染第一个无效输入? text/color 应该显示什么(基于 ValidationException 中的数据)?我们是否将错误渲染为 popup/textView/tooltip?在做出所有演示决策并创建新模型后,View
就这样!使用该模型渲染。 - 还有一点,在Domain层,验证码应该放在哪里?在用例函数或模型(为什么不)本身?恕我直言,应该有具有通用验证逻辑的无状态通用 Interface/Class。在那之后,每个用例 class 都可以实现 ValidationInterface 或将其作为 Class 对象注入。如果多个用例需要相同的验证,验证控制逻辑将被复制。如果我们将验证逻辑放在模型本身中会发生什么?模型将实现 ValidationInterface( 仅具有无状态纯函数!)并具有
fun validate():ValidationOutcome
函数。我认为将业务模型的验证逻辑放在自己身上不是问题。所有用例只会调用model.validate()
。 Model 和 ValidationOutcome 之间存在依赖关系。
我想这个 example of Uncle Bob quoted by @sufian and this article 在做出那个决定时会很有用。
Naoto 指出,正如 Clean Architecture 按层划分责任一样,每一层都有自己的验证逻辑。
在每一层中,系统应该拒绝违反其层责任的输入。因此验证的含义根据其上下文而有所不同。
在应用层,作为验证,我们必须确保领域对象可以接收输入。我们应该拒绝域对象无法接收的输入。
比如缺少一些强制参数时,应该拒绝,因为领域对象没有办法接收像那个参数。