[CURD] 原子/错误处理/协议
[一些微小的思考,来的快,也去的快,记录一下]
描述
在任何一个系统中,我们都离不开最后的数据读写并持久化问题。
数据分为文件数据和结构化数据,文件数据可以是直接使用操作系统提供的文件系统进行读写,也可以是使用分布式的GFS这样的系统读写;而结构化数据,从简单的配置数据,类似XML/JSON/INI等简单结构化数据读写,到基于SQLITE这样的单机数据库读写,基于MYSQL/SQLSERVER这样的Server/Client关系型数据库读写,或者再后来的MongoDB/RethinkDB等No-SQL数据库读写,再到更为复杂的分布式元数据管理系统如ZooKeeper等的读写,不一而足。
无论哪种类型的读写,底层数据在上层做为一种资源(Resource),在接口上都表现为基本的“创建/更新/读取/删除”四种类型的基本操作,也即常说的CURD,或增删查改。很多时候,大部分的软件在界面设计上也会呈现出“查看信息/搜索信息/更新信息”等基本的操作。可以说,CURD几乎是无处不在。基于数据库的“Insert/Update/Select/Delete”的便利写法也导致了我们对CURD的习以为常。然而,写一个合格的CURD确是需要理解几个关键的语义。
搜索
- 搜索中文关键字
CURD
:https://baike.baidu.com/item/CURD - 搜索维基百科
CURD
: https://en.wikipedia.org/wiki/Create,_read,_update_and_delete - Google搜索英文,找到一个不错的文章:https://stackify.com/what-are-crud-operations/
- 问答社区上一个CURD和Rest对比的帖子引起我的注意:https://softwareengineering.stackexchange.com/questions/120716/difference-between-rest-and-crud
原子
上述几个链接里对CURD的语义有足够的说明。但其中有一些地方可以忽略不看:“利用SQL存储过程对CURD优化”,这个我认为不必理会。我比较关心的一个重要方面是CURD的原子性:“一个操作要么成功,要么失败”,为了达到这个目的,一般通过数据库的事务机制保证,一个操作可能不只涉及一个语句操作。上面第3个链接里把针对CURD的操作又作了一个分类,这个分类的操作接口是比较完备的:
在实现中,需要做一定的分层。例如:基本读写/错误转换/组合操作/对外接口。
错误码
CURD的接口,需要合理设计操作的错误码,举例,同样是查找动作,如果找到的数据集为空,应该返回怎样的错误码更合适?至少有如下两个例子可以区分:
- 如果一个资源A是独立的,没有逻辑上隶属于其他资源,那么可以返回( result.success, [])表示搜索到的结果为空。
- 如果一个资源A逻辑上隶属于资源B,那么一个findAByBID(b_id)可以有两层:
- 先查找B,如果找不到B,返回(result.not_found, []), 表示这个资源根本就不存在(所属的父资源B不存在)
- 如果B存在,继续查找A的记录里含有b_id的,找不到则返回(result.success, []) (所属的父资源B存在,但是A资源里并没有对应的关联记录)
这只是一种可能的错误码约定,这样经过设计的错误码能正确反馈一个CURD操作发生错误时的原因,便于上层进一步处理或者溯源。
协议
一个资源在创建的时候,往往只有初始信息,很多字段会留空。而一些其他字段需要在更新的时候才能确定。那么C/U操作要合理设计对应的接口参数,在这个取舍中体现的是对资源的生命周期的理解。在理解的基础上设计合理的接口协议。
一个资源被删除,是应该直接把它从数据库里实际删除掉,还是只是把它标记为'delete'状态?这又是跟该资源的实际使用/运维需求相关。