域与数据持久层中的干净架构验证?


13

我正在研究干净的软件,因此,我极大地重新思考了如何设计和编写软件的大量方法。

不过,我仍在努力处理业务规则,例如“保存某些项目的更新,首先加载我有权查看/编辑的所有项目列表,确认该项目在列表中,并且该商品类别当前未被锁定(以及其他规则等)。”,因为这是(复杂但非典型的)业务规则,因此应在应用程序域中进行处理,而不是将业务逻辑推送到db / persistence层。

但是在我看来,为了有效地检查这些条件,通常最好使用精心设计的数据库查询来最好地处理它,而不是将所有数据加载到应用程序域中...

如果不进行过早的优化,有什么推荐的方法或鲍勃叔叔的文章处理这个问题?还是他会说“在域中进行验证,直到出现问题”?

除了最基本的用例之外,我真的在努力寻找任何好的示例/样本。

更新:

大家好,谢谢您的答复。我应该更清楚一些,我已经写了很长时间(主要是Web应用程序)软件,并且肯定已经经历并同意您共同描述的所有主题(通过后端验证,一般不信任客户端数据仅在需要时才追求原始效率,但是在可用时承认db工具的优势,等等),并且已经经历了开发人员学习生命周期的“全部整合”以“构建具有N层应用程序的大型胖控制器”代码趋势,现在真的很喜欢研究干净/单一责任的样式等,这主要是由于最近有一些项目随着项目的发展以及对客户的进一步要求逐渐演变为笨拙且分布广泛的业务规则。

特别是,我在构建REST的API面向客户和内部使用功能的背景下,看着清新的风格建筑,其中许多业务规则可能是比基本上你在网上看到每一个例子更复杂(甚至由Clean / Hex体系结构专家自己完成)。

所以我想我真的是在问(并且没有清楚地说出)Clean和REST api会如何放置在一起,这些天您看到的大多数MVC东西都带有传入的请求验证器(例如.NET中的FluentValidation库),但是其中许多我的“验证”规则不是“这是少于50个字符的字符串”,而是更多的,“如果某个相关对象当前已被Team X锁定,则该调用此用户案例/交互者的用户可以对数据集执行此操作吗?直到本月下旬,等等”。。。那些涉及深层次的验证,其中适用于业务域对象和域规则的数量。

我是否应该将这些规则分解为特定类型的Validator-object类型,以与每个用例交互器一起使用(受FluentValidator项目的启发,但涉及更多的业务逻辑和数据访问),我是否应该将验证视为类似于Gateway,我应该将那些验证放入网关(我认为这是错误的)等。

作为参考,我会去像几篇文章这样,但马蒂亚不讨论验证了。

但是我想我的问题的简短答案很像我接受的答案:“这绝非易事,要视情况而定”。


2
“正确”与“实用”之间通常会有区别。如果有选择,您更喜欢哪个?
罗伯特·哈维

“加载所有项目列表”看起来并不像业务规则,似乎过多地研究了实现细节。如果您可以通过使用数据库查询来满足规则,而无需加载任何内容,那么为什么规则会显示“ load”?
停止伤害Monica

Answers:


31

数据输入的验证是所有人开始尝试使其变得纯净和干净(并且如果他们对此很聪明的话)并最终放弃的事情之一,因为存在许多相互竞争的问题。

  • UI层必须在客户端页面/表单上在那里进行某种形式的验证,以便向用户提供实时反馈。否则,当交易在网络上发布时,用户将花费大量时间等待反馈。

  • 由于客户端通常在不受信任的机器上运行(例如,几乎在所有Web应用程序中),因此必须在信任代码的服务器端再次执行这些验证例程。

  • 由于输入限制,某些形式的验证是隐式的。例如,文本框可能只允许数字输入。这意味着您可能没有“它是数字吗?” 验证器在页面上,但是您仍然需要在后端的某个位置验证器,因为可以绕过UI约束(例如,通过禁用Javascript)。

  • UI层必须在服务边界进行某些形式的验证(例如,Web应用程序中的服务器端代码),以使系统免受注入攻击或其他恶意数据输入的侵害。有时,这种验证甚至不在您的代码库中,例如ASP.NET请求验证

  • UI层必须执行某种形式的验证,才能将用户输入的数据转换为业务层可以理解的格式。例如,它必须在适当的时区将字符串“ 6/26/2017”转换为DateTime对象。

  • 业务层应该执行大多数形式的验证,因为从理论上讲,它们属于业务层。

  • 某些形式的验证在数据库层更有效,尤其是在需要引用完整性检查时(例如,确保状态代码在50个有效状态的列表中)。

  • 由于并发性的考虑,某些形式的验证必须在数据库事务的上下文中进行,例如,保留唯一的用户名必须是原子的,以便其他一些用户在处理过程中无法抓住它。

  • 某些形式的验证只能由第三方服务执行,例如,在验证邮政编码和城市名称一起使用时。

  • 在整个系统中,可能在多层进行空检查和数据转换检查,以确保在存在代码缺陷的情况下采取合理的故障模式。

我已经看到一些开发人员尝试将业务层中的所有验证规则进行整理,然后让其他层调用它以提取业务规则并在另一个层上重构验证。从理论上讲,这将是很棒的,因为您最终只能获得单一的真理来源。但是,我从来没有见过这种方法除了不必要地使解决方案复杂化之外,没有做任何其他事情,而且通常以非常糟糕的结尾。

因此,建议您杀死自己,弄清楚验证代码的去向-在即使是中等复杂问题的实用解决方案中,验证代码最终也会在多个地方使用。


如果您认为自己是UI管理所有用户的反馈,则可以将大多数验证检查推送到数据库中,而只保留您在业务层无法执行的操作。如果您设计一个完整的后端API并进行非常详细的消息故障排除,则会出现问题。
Walfrat

2

验证是业务层的一部分。

关键是:DAO中的业务逻辑将使DAO的概念无效。如果您从另一个用例中调用业务操作,则在更高的层进行验证将导致冗余验证。

也许您可以评估UI中的一些安全性。但这是可选的,因为受保护的域对象将发挥重要作用。在UI中,根据当前登录用户所拥有的权限,使“组件”可见或不可见。但这只是用户体验的一部分。您不希望用户每次尝试执行不允许执行的操作时都会遇到安全异常。


2

您可能想检查一下有关谁在进行验证的观点。是数据库,您知道您正在使用数据库吗?还是它是一项服务(恰好由DB操作支持和控制)。在我的项目中,每个聚合根都有一个可以读取它的组列表和一个修饰符列表。当代码查找用户可以看到的特定根目录或根目录列表时,所有详细信息都将隐藏在带有用户ID和查询上下文其他部分的服务后面,例如图块以“ blah”开头。代码并不关心数据库是否执行了存在检查,以查看用户组是否存在于阅读者组中。它仅期望根据仅由合同定义的服务所提供的内容,列出包含或不包含内容的列表。

这适用于所有层。验证的一致性是关键。尽可能多地将验证放在域中。使用您的API返回约束。最后,我不认为约束来自X库或Z存储,而是来自服务。


0

如果以数据库查询的形式最简单,最清楚地表达了某些验证逻辑,那么继续吧,您将获得答案。但是,只有在存在已知的性能问题时才应该关注效率,否则这是过早的优化。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.