如何在 Web 服务更新消息 (DTO) 中使用可选属性?
背景
假设您有一个 (SOAP) Web 服务 BookService
,用于管理图书馆中的图书。在信息模型中,假设 Book
实体具有以下属性:
id
author
publisher
title< /code>
shelfId
为了操作数据,定义了四个 Web 服务操作:
AddBook
GetBook
UpdateBook
DeleteBook
为每个操作定义请求和响应消息。然而,更新消息XML模式的设计更为复杂。我们希望实现以下品质:
- R1:可以重置/删除属性的先前值。例如,您不再将这本书保留在图书馆中,因此希望重置/清空/删除该特定书籍的属性
shelfId
的属性值。 - R2:避免 Web 服务中的闲聊。请参阅 反模式闲聊服务。
- R3:为未来对并发控制和乐观锁定的要求做好准备。我们可能希望最小化(或消除)基于旧信息进行更新的风险。
设计替代方案
我看到了三个主要替代方案,其中一个具有多个子选项,用于设计更新消息:
- 发送整个业务文档。 遗漏的元素(架构中具有
minOccurs="0"
)OR 元素显式设置为 null,即
,将被解释作为删除之前的值。 - 突出显示更改或仅发送差异。
- 发送整个业务文档,但使用特定于此目的的属性标记修改的元素。示例:
。然后,服务提供者仅更新那些标记为脏的元素,并忽略其他元素。海明威 - 在消息架构中,将除标识符
id
之外的所有元素设置为minOccurs="0"
。消费者仅发送那些要修改的元素。留出元素在语义上不能被解释为删除。为了删除值,必须使用显式 XMLNULL
值。示例:
。 - 发送整个业务文档,同时提交之前阅读过的文档的副本。然后,提供者可以比较两个文档,并仅更新新文档与先前文档不同的属性。
- 发送整个业务文档,但使用特定于此目的的属性标记修改的元素。示例:
- 定义多个操作。不要只使用一个操作,
UpdateBook
,而是根据您认为必须更新的元素定义多个操作,例如UpdateBookAuthor
>、UpdateBookPublisher
等等。其中每一个都只有强制元素,并且要删除元素,请使用 XML 的显式 NULL,例如
。
讨论
Alt 3 的优点是简单易懂,但缺点是当 Book 实体应该更新。这使得服务变得“喋喋不休”(参见上面的 R3),从而导致性能下降。
Alt 2比Alt 1更复杂,但是Alt 2有一些与乐观并发控制相关的优点:
- 对于使用乐观锁定的情况每个字段的时间戳/版本存储在数据库中(例如
authorVersion
)=> Alt 2 提供了一种让多个用户修改同一本书author 和publisher
> 同时出现故障的风险较小。 - 对于将整个
Book
的单个时间戳/版本的乐观锁定存储在数据库中的情况 => Alt 2 相对于 Alt 1 没有真正的优势。即使更新只修改一个字段,请求的版本号太旧也会导致错误。 - 对于不使用并发控制或乐观/悲观锁的情况=> Alt 2 比 Alt 1 被旧数据覆盖的风险更小,但其他不一致的更改可能会产生问题。
还有另一种情况,Alt 2(和Alt 3)比Alt 1更有优势。消费者可能不会存储有关 Book
实体的所有数据。例如,如果机器人在更新书架信息时不需要跟踪(缓存)有关作者的信息,而只需要跟踪(缓存)书架,则可以更有效地对从书架上挑选书籍的机器人进行编程。
Alt 2.3 中的方法的优点是,使用者提交以前版本的完整副本而不是版本号或时间戳,数据库中不需要专门的列来存储版本号或时间戳。
总而言之,我想说 Alt 2.2 在大多数情况下看起来是最有吸引力的。这里的挑战只是反序列化 XML 的框架必须能够区分遗留元素和显式设置为 NULL 的元素,例如
。 在此处查看有关此主题的帖子。
问题
您会选择哪种替代方案?您还看到其他更好的选择吗?您对讨论有何看法?
BACKGROUND
Assume you have a (SOAP) web service, BookService
, managing books in a library. In the information model assume that the Book
entity has the following attributes:
id
author
publisher
title
shelfId
In order to manipulate the data four web service operations are defined:
AddBook
GetBook
UpdateBook
DeleteBook
Request and response message are defined for each operation. However, the design of the update message XML schemas is more complicated. We would like to achieve the following qualities:
- R1: Possibility to reset/delete previous values of an attribute. E.g. say that you no more will keep the book in the library and thereby would like to reset/empty/remove the attribute value of the attribute
shelfId
for that specific book. - R2: Avoid chattiness in the web services. See the anti-pattern Chatty Services.
- R3: Prepare for future requirements on Concurrency Control and Optimistic Locking. We may want to minimize (or remove) the risk of having updates made on based of old information.
DESIGN ALTERNATIVES
I see three mayor alternatives, of which one has several sub-options, to design the update message(s):
- Send the entire business document. Left-out elements (having
minOccurs="0"
in the schema) OR elements explicitly set to null, i.e.<shelfId xsi:nil="true"/>
, would be interpreted as deletion of the previous values. - Highlight the changes or send only the diff.
- Send the entire business document, but mark the modified elements using an attribute specific to this purpose. Example:
<author dirty="true">Hemingway<author/>
. The provider of the service then updates only those elements marked as dirty and ignores the other ones. - In the message schema, set all elements, but the identifier
id
, to haveminOccurs="0"
. The consumer sends only those elements which are to be modified. A left-out element must not semantically be interpreted as a deletion. In order to delete a value the explicit XMLNULL
value must be used. Example:<shelfId xsi:nil="true"/>
. - Send the entire business document, but also submit a copy of the previously read document. The provider can then compare the two documents and update only those attributes for which the new and the previous documents differ.
- Send the entire business document, but mark the modified elements using an attribute specific to this purpose. Example:
- Define multiple operations. Instead of only using one operation,
UpdateBook
, define multiple operation based on which elements you believe will have to be updated, e.g.UpdateBookAuthor
,UpdateBookPublisher
and so on. Each of these will have only mandatory elements and for removal of elements use the explicit NULL of XML, e.g.<shelfId xsi:nil="true"/>
.
DISCUSSION
Alt 3 has the advantage of being simple to understand, but the drawback is that consumers would need to invoke multiple operations in case multiple fields in the Book
entity should be updated. This makes the service "chatty" (see R3 above), giving performance penalties.
Alt 2 is more complex than Alt 1, but there are some advantages of Alt 2 related to Optimistic Concurrency Control:
- For situations when optimistic locking with timestamps/versions for each field is stored in the database (e.g.
authorVersion
) => Alt 2 provides a way of letting multiple users modify different parts, such asauthor
andpublisher
, of the sameBook
concurrently with less risk of having faults. - For situations when optimistic locking with one single timestamp/version for the entire
Book
is stored in the database => No real advantage of Alt 2 over Alt 1. Even if an update only modifies one field, a too old version number of the request would result in a fault. - For situations when no concurrency control or optimistic/pessimistic locking is used => Alt 2 gives less risk than Alt 1 of overwriting with old data, but still other inconsistent changes may give problems.
There is yet another situation in which Alt 2 (and Alt 3) gives an advantage over Alt 1. The consumer may not store all the data about the Book
entity. For example, a robot picking the books from their shelf's may be programmed more effectively if it doesn't need to keep track (cache) on the information on the author, but just the shelf, when updating the shelf information.
An advantage of the approach in Alt 2.3, where the consumer submits entire copies of previous versions instead of version numbers or timestamps, is that no dedicated column in the database is needed for version number or timestamp.
To sum it up I would say Alt 2.2 looks like the most attractive one for most cases. The challenge here is just that the frameworks deserializing the XML must be able to distinguish a left-out element from an element explicitly set to NULL, e.g. <shelfId xsi:nil="true"/>
. See a post on this topic here.
QUESTION
Which of the alternatives would you go for? Do you see other, better alternatives? What do you think of the discussion?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如您已经讨论过的,替代方案 2.2 似乎相当可行。然而,minoccurrs=0 和 nil 之间的区别经常被框架忽略,并且对于许多用户(可能是这个接口的用户)来说也很难理解。因此,我会选择一种变体,在一条更改消息中,当您希望使每个属性无效时,您可以明确地标记每个属性。
etag 可能是用于乐观锁定的一种标准。我认为您已经广泛讨论了锁定机制的含义。它应该按照你描述的方式工作。
Alt 1 在消息设计和使用/实现方面都简单得多,如果流量不是问题并且整个对象的乐观锁定就足够了(恕我直言,这是最常见的情况),那么它也可以很好地工作。
您还应该考虑到,对于某些业务流程,不同属性的更改是相互依赖的,因此对属性进行单独的版本控制是没有意义的,因为即使只有另一个属性在同一时刻已过时,一个属性的更改也可能是错误的。改变的时间。
如果情况确实如此,那就是替代项 1 的另一个原因。
As you have already discussed, alt 2.2 seems to be rather feasible. However, the distinction between minoccurs=0 and nil is often ignored by frameworks and is also hard to understand for many users (potentially of this interface). Thus I would go for a variation, where in one change message you mark with every attribute explicitly when you want them to be nullified.
etag could be one standard to be used for optimistic locking. I think you have discussed the implications of locking mechanisms already extensively. It should work the way you describe.
Alt 1 is a lot simpler, both in message design and usage/implementation and if traffic is not an issue and optimistic locking for whole objects is enough (which is most often the case imho), that could also work well.
You should also take into account that with some business processes, changes of different attributes are interdependent so it doesn't make sense to have separate versioning for attributes, since a change at one attribute can be wrong even if only another attribute was outdated at the time of change.
If this can be the case, it is another reason for alt 1.