当我刚开始学习编程和web开发时,就听说过 CRUD。这差不多就是我们要用到的方法。我会花时间思考实体是否是 “资源”,以及如何编写基于 crud 的优秀应用程序,在这些应用程序中,每个资源要么被创建、读取、更新,要么被删除,仅此而已。本质上,最好的描述就是 “CRUD 思维”。
创建,读取,更新,删除,足够简单 — 这也正式问题所在。
您上次仅从存储管理的角度考虑域名是什么时候呢?
但我可以理解为什么初学者喜欢这种方法。CRUD 思维可以很好地转化为现有的编程概念,如 SQL 的 INSERT、 SELECT、 UPDATE、 DELETE 或 HTTP 的 GET、 POST、 PUT/PATCH、 DELETE。
基于 CRUD 的应用程序有什么问题?
在我看来,主要问题在于创建基于 CRUD 的界面时,我们完全忽略了用户的意图。意图是很重要的一个点,作为开发者,我们不能只关注开发本身。
一旦开始以类 CRUD 的方式设计应用程序的接口(如 HTTP 端点) ,就会出现问题。
顺便说一下,这里有一篇深入分析 CRUD 优于 REST 的文章。
举个例子,假设你创建了下面这样一个 Author 模型,并且您正在开发一个 web API,对 author 执行一些操作,并将其存储在数据库中。
一个 Author 模型
这时有一个作者希望有个额外的笔名 — 解释一下,笔名就类似作者的 “伪” 名,但是这个应用程序只支持 PATCH api/authors/{ id } 以完整的作者模型作为输入。
起初,你可能认为这没什么大不了的。
但是,这对应用开发人员来说是一个巨大的烦恼。没有机会猜测需要更新什么,因此实现可能会变得过于冗长,更新每个属性,如下所示。
这是相当混乱的,我们只想添加一个笔名而已。想象一下,如果我们添加了错误处理和条件分支,它将变得非常难以维护。
当我还是一名软件顾问时,我注意到一些开发人员试图避免更新每个属性。他们的解决方案是在相同的方法中增加一个额外的层:他们会收到一个告诉他们用户意图的属性。
然后我们就有了如下的代码:
这个想法不错。但我认为它并没有正确解决问题,因为 圈复杂度和认知复杂度 开始无控制地上升。代码可读性也不高。
显然,尽管保留了 CRUDish API,但基于任务的方法却出现了。这种方法暴露出一个明显的陷阱:模型混乱。
作为 API 的客户机,我仍然可以生成完整的 UpdateAuthorConditionally 模型。也就是说,使用数据填充所有属性,但由 UpdateReason 决定实际更新哪些 author 属性。
因此,困惑的部分是:如果我只想添加一个新的笔名,为什么还要填 email 属性呢?
有一个简单的解决方案
停止基于 CRUD 的接口设计。相反,基于任务的接口是一个非常好的选择,您的客户和同事将会高度赞同。
使用上面的类 crud 接口,我们可以提取以下端点:
基于任务的端点
它有更多的方法、类,总体上代码行数更多了。那么它究竟好在哪里呢?
更加干净了,传统的条件分支语句已经不复存在了。
每个方法都有明确的职责,每个方法只做一件事, 每一个都能独立于其它方法单独开发。
一旦开始应用这种方法,圈复杂性和认知复杂性将大大降低。
代码将更容易阅读和维护。猜测性的工作已经不需要了,你和客户都知道什么时候更新什么。
你只是在属性级别上进行 CRUD 操作!
确实看起来有点像,那是因为我直接将基于 CRUD 的接口转换为了基于任务的接口。但是,希望这能帮助许多人理解,因为除了 CRUDy API 中已经确定的任务之外,没有其它 “新” 任务。
此外,我相信大多数人都可以从实现中抽象出这个概念:在开发应用时,寻找与客户可能有的意图相匹配的更专门的端点。
但是,让我明确地说明一下:我并不是说每个对象属性都应该有一个端点。我的意思是你应该有一个端点来匹配客户的意图和他们想要执行的操作。
现在需要调用5个端点,而不是1个!
好了,现在你知道每个端点会做什么了。
当我为丹麦教育部创建一个应用程序时,我们不得不与第三方服务提供商合作,这个提供商将拥有学校、教师、班级和学生的所有数据。
在这个项目开始的几周里,我们需要增加一所学校的学生。因此,我只是调用他们的端点,并提供了学生的 XML 数据。不幸的是,这该死的东西抹掉了整个学校的数据,最后加了一个新学生。所以,结果就是一所只有1个学生的小学(然而他们的端点却根本没有提到这种行为)。
如果他们只有一个端点,比如 POST/PATCH api/schools/{ id }/AddStudent,我们就可以避免这个麻烦。
有意思的是: 由于第三方 API 很难用,大多数学校会向第三方发送一个 excel 表格,然后他们手动处理这些 excel 表格。
CRUDy 应用有时候也是没问题的?
当然,基于 CRUD 的应用程序还是有它们的用武之地的,尽管我主张消灭它们。毫无疑问,其主要优势在于开发速度。
那些短期的、一次性的、概念验证(proof-of-concept,PoC)类的应用就比较适合使用 CRUD 模式。然而,正如我们所知道的,没有所谓的短期或一次性的事情。
如果事实证明是可行的并且和可取的,POCs 通常会升级到生产状态。
总而言之
初学者经常学习如何创建一个基于 CRUD 的应用程序界面,尤其是在学习如何创建 web API 时,但他们很少花时间反思这种看似简单的方法可能带来的一些困难。
应用基于任务的方法是对 CRUDy 接口一个很好的替代。基于任务的接口传达了意图和目的,允许每个端点单独演进,比 CRUD 更易于维护。