为什么域模型不应该用作REST API中的资源?

Ada*_*ion 11 api rest domain-driven-design domain-model

我发现一个声明,即根据DDD设计的域模型不应该用作REST API()中的资源.

很明显,REST API是应用程序的契约,而域模型是实现的一部分,因此最好将这两个事物分开,以便域模型中的更改不会自动意味着更改REST API.

但是,我认为在小型项目(REST API只有一个消费者 - 由一个团队开发的javascript前端)的情况下,使用单独模型的好处并不能证明分离模型的成本(不同的类 - 域模型和资源表示和模型之间的映射代码).显然,域层不能引用REST特定的基础结构代码(以保持关注点分离).

域和REST模型应该分开吗?

the*_*Dmi 11

使用DDD时,REST API应始终与域模型分离.

这样做的主要原因是简化 - 您不希望通过API将域模型的复杂性泄露给客户端.否则,客户需要了解您的域的细微差别和复杂性,这很可能使API难以使用.

使用DDD的主要驱动因素是一个复杂的问题域,因此这始终是一个问题.

但是,我认为在小项目(......)的情况下,拥有单独模型的好处并不能证明分离模型的成本(......).

我同意有些项目的分离域模型和REST API过度工程化.但是,这些案例不适合DDD,因为您不会从DDD中获益足以证明其成本合理.


gui*_*e31 5

为什么不应将域模型用作REST API中的资源?

因为网络与核心域层完全不同。由于HTTP仅包含少量动词,因此您实体中的方法特别难以翻译。如果要通过REST公开应用程序,则必须将域过程塞入HTTP,这通常意味着要做出妥协并设计与域实体不同的资源。

当然,如果要进行HATEOAS,您应该在HTTP客户端和服务器之间交换的消息中以及域应用协议中找到通用语言中的术语,但是网络必定会扭曲域表示形式。

REST的目的不是重新创建您的域及其过程的高保真模型,而是以HTTP兼容的方式交付它们,同时尽可能减少翻译的损失。然而,它仍然是翻译。


inf*_*rno 5

我认为 REST API 的主要好处是为(通常是服务器端而不是 SPA)第 3 方 REST 客户端提供服务。如果您使用 HATEOAS 和其他自描述消息解决方案(例如 RDF),那么由于 REST API 的更改,REST 客户端将更难崩溃。对于小型项目 - “REST API 只有一个使用者 - javascript 前端,由一个团队开发” - 我认为不值得付出努力来拥有一个合适的 REST API。大多数人使用它的简化版本,我称之为 CRUD API,这些可能对这些项目有好处。

CRUD 资源和贫血域模型的域对象之间可以存在 1 对 1 的映射。如果我们谈论的不仅仅是 CRUD 方法的真实对象(而不是数据结构),那么您必须在 resources.verb 和 object.method 之间进行转换,例如:

POST /dogs/{id}/barking
 -> domain.dog.bark()
Run Code Online (Sandbox Code Playgroud)

如果我们讨论的是涉及多个域对象和工作单元(事务)的更复杂的事情,那么您需要为应用程序服务添加另一层,否则您会将包括事务处理在内的整个复杂操作移至客户端。在这些情况下,您可以在resource.verb和applicationService.operation之间进行转换,例如:

POST /dogs/{id1,id2,..}/barking
 -> dogService.multiDogBark(...)
 -> UnitOfWork{domain.dogs[ids[i]].bark()}
Run Code Online (Sandbox Code Playgroud)

我认为大多数开发人员将这种 CRUD 服务 + 贫血域模型方法与 REST 服务 + 域模型方法混淆了,这就是为什么会问这个问题,这就是为什么有许多“REST”框架添加 1:1 域对象 - CRUD 资源映射或者甚至是 ORM 实体 - CRUD 资源映射。我发现这种趋势非常具有破坏性,我认为开发人员仅从短文或问答网站上肤浅地学习某些技术的主要原因,而不是阅读书籍和论文,在那里他们可以获得实际主题的深入知识。我认为这是Y+一代的问题,因为数字技术的使用,我们正在失去阅读长文本的能力。我们习惯于即时奖励,而不是长文本给出的延迟奖励......