RESTful API是否倾向于鼓励贫血领域模型?


34

我正在一个项目中,我们试图将域驱动设计和REST都应用于面向服务的体系结构。我们不必担心100%符合REST。最好说我们正在尝试构建面向资源的HTTP API(Richardson REST成熟度模型的第2级)。但是,我们试图远离RPC样式的HTTP请求的使用,即,我们尝试根据RFC2616实现我们的HTTP动词,而不是使用POSTdo来实现IsPostalAddressValid(...)

但是,对此的强调似乎是以我们尝试应用域驱动设计为代价的。只有GETPOSTPUTDELETE和其他一些很少使用的方法,我们倾向于建立眉头服务和眉头服务往往有贫血的域模型。

POST:接收数据,对其进行验证,然后将其转储到数据中。GET:检索数据,然后将其返回。那里没有真正的业务逻辑。我们还在服务之间使用消息(事件),在我看来,大多数业务逻辑最终都围绕该消息构建。

REST和DDD是否处于某种程度的紧张状态?(或者我在这里误解了什么?我们是否可能在做其他错误?)是否有可能在面向服务的体系结构中构建强大的域模型,同时避免RPC样式的HTTP调用?


1
POST被故意设计为“故意模糊”; POST的结果是特定于实现的。是什么使您无法执行Twitter和其他API设计人员的工作,并根据自己的特定要求在API的非CRUD部分中定义每个POST方法?
罗伯特·哈维

@RobertHarvey我们已经将POST视为一个创建。在展望标准再次,也许这是过于简单化。例如,您认为POST这样做IsPostalAddressValid(...)是否适合“为数据处理过程提供数据块,例如提交表单的结果”?
卡扎尔克2014年

那是因为没有CREATE动词(也没有UPDATE动词)。我坚持认为这些动词是标准中遗漏的(无论出于何种原因),这就是为什么您必须为其他所有内容选择POST的原因。在这种情况下,您的“数据处理过程”是检查邮政地址并返回与该分析结果相对应的值的过程。
罗伯特·哈维

1
@RobertHarvey:我相信POST和PUT / PATCH只是您一直想要的CREATE和UPDATE动词。它只是以不同的方式命名,因此即使在非RESTful设计中,动词也仍然具有一定意义。
Lie Ryan

@LieRyan:我会给你的。我只是认为CRUD从定义上暗示了贫乏的数据模型。例如,如果您处于MVC的M中,则可以保留某些行为,但肯定不能跨异构系统。除了CRUD之外,其他所有内容都需要POST。
罗伯特·哈维

Answers:


38

马丁·福勒(Martin Fowler)的分布式系统第一定律:“不要分发您的对象!” 远程接口应该是粗粒度的,而内部接口应该是细粒度的。通常,富域模型仅适用于有限的上下文

REST API将两个具有各自内部模型的不同上下文分开。上下文使用“贫乏”对象(DTO)通过粗粒度接口(REST API)进行通信。

就您而言,这听起来像是您尝试将上下文扩展到REST API的边界上。这可能导致细粒度的远程接口或贫乏的模型。根据您的项目,它可能是问题,也可能不是问题。


1
Fowler有很多好的想法,但是让我们不要忘记原始的EJB规范和实现是什么灾难。直到后来他们才发现,像getName()这样的每个次要操作的低级方法调用都是网络/负载的噩梦。粗粒度的界面成为必经之路,它带来了整个实体图/消息在动词+名词上下文中发送和接收的概念。
Darrell Teague 2014年

9

POST被故意设计为“故意模糊”; POST的结果是特定于实现的。是什么使您无法执行Twitter和其他API设计人员的工作,并根据自己的特定要求在API的非CRUD部分中定义每个POST方法?POST是万能的动词。当其他动词都不适合您要执行的操作时,请使用它。

换句话说,您的问题可以等同地提出为““智能”对象是否鼓励RPC样式的设计?” 甚至Martin Fowler(创造了“贫血领域模型”一词)也承认,裸露的DTO确实有一些好处:

将行为放入域对象不应与使用分层将域逻辑与持久性和表示责任之类的东西分开的可靠方法相矛盾。域对象中应该包含的逻辑是域逻辑 -验证,计算,业务规则-随便您如何称呼它。

关于Richardson成熟度模型,您无需担心“贫血领域模型”就可以升级到3级。请记住,您永远不会将行为转移到浏览器(除非您计划通过模型注入一些Javascript)。

REST主要是关于机器独立性。在您希望端点代表资源的程度实现REST模型,并使API的使用者能够以标准方式轻松访问和维护这些资源。如果那看起来贫血,那就这样吧。

另请参阅
我需要更多动词


我认为这肯定解决了RFC2616方面的问题。对于我们正在尝试面向资源的事实,即至少试图在REST的Richardson成熟度模型中达到2级的事实,该怎么办?
卡扎尔克2014年

1
我通过martinfowler.com/articles/richardsonMaturityModel.html阅读。您可以进入第3级,而无需担心“贫血领域模型”。请记住,您永远不会将行为转移到浏览器(除非您计划通过模型注入一些Javascript)。
罗伯特·哈维

4

REST API只是表示层的一种类型。它与域模型无关。

您发布的问题来自您的困惑,即您需要以某种方式使彼此适应。你不知道

您将域模型映射到REST API的方式与通过ORM将域模型映射到RDBMS的方式相同-必须有此映射层。

域←ORM→RDBMS
域←REST映射→REST API


3

恕我直言,我不认为它们倾向于鼓励贫血领域模型(ADM),但它们确实需要您花一些时间来思考。

首先,我认为ADM的主要特征是它们几乎没有行为。这并不是说系统没有任何行为,只是它通常在某种Service类中(请参阅http://vimeo.com/43598193)。

当然,如果该行为在ADM中不存在,那怎么办?答案当然是数据。那么这如何映射到REST API?大概是数据映射到资源的内容,行为映射到HTTP谓词。

因此,您拥有构建丰富的域模型所需的一切,只需要能够查看HTTP谓词如何映射到数据上的域操作,然后将这些操作放入封装数据的相同类中即可。

我认为人们容易遇到问题的地方是,当行为超出简单的CRUD时,即当域的其他部分存在副作用时,他们很难了解HTTP动词如何映射到其域行为。 HTTP请求正在修改资源。解决该问题的一种方法是使用域事件(http://www.udidahan.com/2009/06/14/domain-events-salvation/)。


3

文章是非常相关的问题,我相信回答你的问题。

在上述文章的以下段落中,我总结了一个核心概念,我很好地回答了您的问题:

“在域驱动设计中区分REST API中的资源和域实体是非常重要的。域驱动设计适用于事物的实现方面(包括API实现),而REST API中的资源则驱动API设计和合同。API资源选择不应取决于基础域的实现细节。”


1

我已经看到/构建的几种相当成功的实现方案回答了以下问题:它们如何使用对实体起作用的粗粒度“业务友好”方法来混合动词+名词隐喻。

因此,而不是(已失效的)getName()方法/服务,而是暴露getPerson(),传入标识符类型/ ID之类的东西,并返回整个Person实体。

由于无法充分传达Person实体在此类上下文中的行为(也许也不应该在这样的以数据为中心的上下文中),因此为请求/响应对定义数据(相对于Object)模型是完全合理的服务。

服务和定义的动词本身将为实体添加一些域允许的行为,控件甚至状态转换规则。例如,transferPerson()服务调用中将发生特定于域的逻辑,但是接口本身仅定义输入/输出实体/数据,而没有定义THEIR内部行为。

我不同意这样的作者的观点,例如,转移动词实现属于Person类或与以Person为中心的服务相关联。确实,a 的转移方法Person及其选项(在此简单示例中)将由a更好地定义Carrier,其中Person可能甚至不知道可用的转移方法或转移的方式(谁知道喷气发动机的工作方式)。无论如何)。

这会使Person实体贫血吗?我不这么认为。

对于人内部的特定于人的事物(例如其健康状态),可以(应该)具有逻辑,不应由外部类定义。

但是,根据用例,完全可以接受的是,实体类在某些系统中没有重要/相关的行为,例如运输系统中的座位分配服务。这样的系统可以很好地实现基于REST的服务,该服务处理Person实例和关联的标识符,但永远不会定义/实现其内部行为。


优点--这确实带来了其他答案尚未出现的新观点。
卡扎尔克2014年

0

您是否正在尝试尽可能多地使用POST将模型塞入基本动词集中的问题?

没必要-我知道对于大多数人来说REST意味着POST,GET,PUT和DELETE,但是http rfc表示:

HTTP / 1.1的一组常用方法在下面定义。尽管可以扩展此集合,但是不能假定其他方法可以为单独扩展的客户端和服务器共享相同的语义。

诸如SMTP之类的系统使用相同风格的基于动词的方法,但设置却完全不同。

因此,没有理由不必使用这些动词,您可以使用任何喜欢的动词集(尽管,实际上,您会发现,只要稍加思考,就可以完成基础4中所需的一切)。使REST有别于其他机制的原因在于它实现这些动词的无状态且一致的方式。您不应该尝试在各层之间实现消息传递系统,因为您基本上没有进行REST,那么您正在执行消息传递,RPC或消息队列机制,这无疑会失去REST的优势(即它的简单性使其可以在http连接上很好地工作)。

如果您需要功能全面的复杂消息传递协议,则可以构建该协议(如果可以通过Web进行操作,则REST如此受欢迎的原因是这样),但否则请尝试坚持REST的体系结构设计。

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.