Monday, January 14, 2013

Hypermedia API is about DRY

The hype around Hypermedia API (HATEOAS) just got too much recently. Opponents and advocates cross swords. But critics overlook one main point: Hypermedia API allows you to not repeat youself.

I could describe the topic in terms of "Hypermedia as the Engine of Application State". The "State Engine" directly relates to the issue. But I prefer to use more familiar dictionary.

Consider yet another project tracker. There is a domain rule: only Project Owner is able to delete a user story AND only stories in "initial" state can be deleted. With the "traditional" REST approach the rule gets duplicated several times: on the server side and for each client app. Without such duplication a client cannot reason about visibility of the "delete story" control. Even worse, the rule is implemented in several languages, increasing the likelihood of errors.
# Server side
def deletable_by?(user)
  user.owner? @project && initial?
end
// Android client
private boolean isDeletableBy(User user, Story story) {
  return user.isOwner(story.getProject()) && story.isInitial();
}
...
if (isDeletableByUser(user, story)) {
  renderDeleteWidgetFor(story);
} else ...

// JavaScript client
function isDeletableBy (user, story) {
  return user.isOwner(story.project) && story.isInInitialState()
}
...
<% if (isDeletableBy(user, story) { %>
  <%= renderDeleteWidgetFor(story) %>
<% } %>
With HATEOAS the rule is implemented once and only once. It doesn't spread domain logic across clients. They become fairly "silly".
# JSON generation on the server side
if story.deletable_by? current_user
  json_builder.delete_link_for story
end
// Android client
if (story.hasDeleteLink()) {
  renderDeleteWidgetFor(story);
} else ...
// Javascript client
<% if (story.hasLink('delete') { %>
  <%= renderDeleteWidgetFor(story) %>
<% } %>
See, clients don't "figure out" or "calculate" button visibility. They render the button only if the "delete" link is present. That's all. It simplifies the iterative development process: changes in domain logic require only server code to be rewritten.
# Changed rule
def deletable_by?(user)
  user.owner? @project
end
JavaScript, Android, iOS clients remain untouched. They still rely on absence/presence of the "delete" link. The "traditional" REST approach requires to rewrite/recompile/redestribute ALL client applications!

Summing up: Hypermedia API keeps domain logic on the server side. It reduces cost and encourages improvements.

Thursday, December 29, 2011

Tuesday, October 25, 2011

The Death of A Genius




Thank  Mikhail Bilenko for help in processing the scanned image.