写点什么

为什么说要用 DDD 替代 CRUD 来设计 API

  • 2017-09-18
  • 本文字数:2187 字

    阅读完需:约 7 分钟

来自亚马逊的高级工程师 James Hood 以简单明了的例子说明了为什么要用 DDD 替代 CRUD 来设计 REST API。

REST 以资源为中心,这些资源以 URI 的形式呈现。在调用 HTTP 时,通过指定一个 HTTP 动词和一个资源 URI 对某个特定的资源进行操作。大部分 REST 框架都提供了生成器,你只要指定一个资源的名字,框架就会为你生成脚手架(scaffold)。不过,这些生成器默认使用的是 CRUD 模型(Create、Read、Update、Delete),它们把资源看成是一系列属性的集合,使用 JSON 或与特定语言相关的数据对象来表示资源,并生成用于对资源进行创建、读取、更新和删除操作的方法。

虽然这给开发者带来了便利,但我觉得这样是有问题的。我不喜欢 CRUD 这样的说法,尤其不喜欢当中的 U。一般的更新操作允许客户端更新资源的任何一个字段,并使用新版本覆盖已有的版本。但如果你允许客户端这么做,那么你的服务 API 就失去了应有的价值。服务层的一个关键价值在于为底层的数据增加业务约束,因此,资源最终都需要带上业务约束。

那么,难道我们就不能给更新操作增加业务约束吗?让我们以最简单的银行账户为例。首先,不能让客户通过调用 API 来随意更新他们的账户余额。另外,账户或许需要最小余额的限制。你在更新操作里做了一些检查,账户余额的变动必须发生在一个指定的范围内。那么这样问题就解决了吗?当然没有。任何一次余额的调整都需要与某种事务相对应,不是吗?是存入、取出,还是转账?如果客户要更改账户该怎么办?这样做是被允许的吗?这样做会不会破坏与其他数据之间的关系?不难看出,你的更新操作很快会让这一切变得像意大利面条一样混乱不堪。我曾经看着一些团队走上了这条不归路,他们试图从更新的字段里去推测客户的意图,结果代码变得像团乱麻。

那么该如何解决这个问题,有其他更好的方案吗?我个人更喜欢基于领域驱动设计(DDD)来设计 API。DDD 的基本思想是说,软件的建模应该发生在真实世界的问题得到解决之后。DDD 使用实体(Entity)和聚合(Aggregate)来描述业务对象,还定义了服务(Service)、值对象(Value Object)和仓库(Repository)等术语,用以解决业务领域或 DDD 边界上下文问题。DDD 不一定非要与 REST 绑定在一起,不过我发现 DDD 与 REST API 近乎天然地合拍,因为 REST 的资源可以很好地与 DDD 的实体映射起来。

那么这意味着什么呢?这意味着,你的 API 应该要以领域对象以及这些对象所提供的业务操作为中心。业务操作是对常规更新操作最好的替代品。我们继续以之前的银行账户为例。

对于银行的 API 来说,账户就是一个领域对象(DDD 里的实体)。这次我们不再使用 CRUD 来为账户建模,而是为账户定义一组业务操作。以下是一系列写入操作:

  • 开户(Open)——新开一个账户。
  • 销户(Close)——注销一个已有的账户。
  • 取出(Debit)——从账户里扣掉一些钱。
  • 存入(Credit)——往账户里存入一些钱。

这些操作都带有一定的业务约束。例如,往一个已经注销的账户里存钱是不被允许的,而在取钱的时候要强制检查最小余额。至于读取操作,我们可以为客户提供一些有用的查询。

  • 加载(Load)——通过账户 ID 加载相应的账户信息。
  • 交易历史——列出账户的交易历史。
  • 客户的账户列表——列出指定客户的所有账户。

在定义好业务操作之后,就可以将它们与 REST API 映射起来。

  1. POST /account ——新开一个账户。
  2. PUT /account/ /close ——注销一个已有的账户。
  3. PUT /account/ /debit ——从账户里扣掉一些钱。
  4. PUT /account/ /credit ——往账户里存入一些钱。
  5. GET /account/ ——通过账户 ID 加载相应的账户信息。
  6. GET /account/ /transactions ——列出账户的交易历史。
  7. GET /accounts/query/customerId/ ——列出指定客户的所有账户。

这些看起来与一般的 CRUD API 非常的不一样,关键在于这些操作具有良好的定义。不管对于服务提供方还是客户端来说,这样的体验都更好。服务提供方不再需要根据更新字段来推测业务操作的意图,业务操作清晰明了,这样的代码更简单,也更容易维护。而对于客户端来说,它们能执行或不能执行哪些操作也是一目了然的。如果 API 具有良好的文档化,比如使用了 Swagger ,那么就可以很清楚地了解到 API 都具有哪些约束。

定义这样的 API 需要做一些前期思考,这不同于使用简单的 CRUD 生成器。如果你打算将 API 暴露成公共端点,就需要在很长的一段时间内为 API 提供支持,最好还是把它看成是一个永久性的事项。我总是建议人们在前期多花一点时间,因为有些东西到了后面就很难修改,而 API 就是一个很好的例子。

所以,在进行 API(REST 或其他)设计时,请停止使用 CRUD 模型。相反,可以通过 DDD 来定义 API,包括领域对象和它们的业务操作。

如果你想看到更多关于领域对象的例子,可以参考 Amazon Web Services 的 API。在 AWS API 开发者指南里,每一个服务都有对应的“关键概念”一节,用以描述领域对象。例如,S3 里定义了 Bucket、Object 和 Permission 等领域对象,Kinesis 里定义了流(stream)和分片(shard)。先了解一个服务的领域对象,再查看 API 参考,然后浏览服务的 API 清单。你会发现,基于这些领域对象构建的 API 在理解和使用上都更加直观。

查看英文原文: There is No U in CRUD


感谢雨多田光对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017-09-18 16:596078
用户头像

发布了 322 篇内容, 共 140.5 次阅读, 收获喜欢 146 次。

关注

评论

发布
暂无评论
发现更多内容

mac办公软件Office 2019激活中文+密钥许可

mac大玩家j

办公软件 Office 2019下载 Office Mac

Web应用防火墙--规则防护 | 京东云技术团队

京东科技开发者

Web应用防火墙 waf 企业号9月PK榜

引领广西鲲鹏产业热潮,助推数字广西发展。广西第四届鲲鹏应用创新大赛总决赛圆满举行!

彭飞

彰显中国气象服务前沿能力,墨迹天气全球雷达融合降水服务产品亮相服贸会

彭飞

软件测试开发高薪私教线下班来啦!名师1V1指导,大咖强化集训

测试人

软件测试 自动化测试 测试开发 线下班

山东布谷科技直播系统源码,系统分析篇:不可或缺的云转码系统

山东布谷科技

软件开发 音视频 系统架构 云转码 直播系统源码

ViTPose+:迈向通用身体姿态估计的视觉Transformer基础模型 | 京东探索研究院

京东科技开发者

人工智能 计算机视觉 Transformer 企业号9月PK榜

说说分布式系统容器化

HelloGeek

Kubernetes 容器 微服务 云原生

DeeTune:基于 eBPF 的百度网络框架设计与应用

百度Geek说

云原生 ebpf 企业号9月PK榜

《可观测性成熟度模型白皮书》正式发布

乘云数字DataBuff

可观测性 智能运维AIOps IT 运维

软件测试/测试开发丨学会与 AI 对话,高效提升学习效率

测试人

程序员 AI 软件测试 测试开发 ChatGPT

技术分享 | 强化学习,让机器像人类一样自我学习

鼎道智联

强化学习

龙蜥白皮书精选:龙蜥安全漏洞管理体系介绍

OpenAnolis小助手

白皮书 系统安全 CVE 龙蜥社区 安全委员会

草图大师 2023中文安装「SketchUp Pro 2023下载」

胖墩儿不胖y

Mac软件 草图大师 三维建模软件 草图大师2023下载

K8s 多集群实践思考和探索

vivo互联网技术

云计算 k8s 多集群

OpenMLDB 基于 Kubernetes 的部署全攻略

第四范式开发者社区

人工智能 机器学习 数据库 开源 特征

揭秘vivo百亿级厂商消息推送平台的高可用技术实践

JackJiang

网络编程 即时通讯 IM

火山引擎 DataTester 首推A/B实验经验库,帮助企业高效优化实验设计能力

字节跳动数据平台

大数据 A/B测试 企业号9月PK榜

Office办公软件下载 Office 2019 for Mac v16.78 beta永久激活版

mac

苹果mac Windows软件 office 2019 办公软件套装

文盘Rust——起手式,CLI程序 | 京东云技术团队

京东科技开发者

cli rust语言 企业号9月PK榜

弹性数据库连接池探活策略调研(二)——Druid | 京东云技术团队

京东科技开发者

数据库 Druid 数据库连接池 企业号9月PK榜

智能合约编写高级篇(二)区块哈希介绍

BSN研习社

开源机密计算平台:蓬莱-OpenHarmony

OpenHarmony开发者

OpenHarmony

七类网线的制作过程

小齐写代码

提高程序并发性能:Java多线程编程

xfgg

Java 多线程

卡奥斯低代码平台新版本体验活动

Openlab_cosmoplat

为什么说要用DDD替代CRUD来设计API_语言 & 开发_James Hood_InfoQ精选文章