November 20, 2014

Service Design Patterns in Rails: Web Service Evolution

This is my fourth post on Service Design Patterns in Rails. These posts have been inspired by the Service Design Patterns book by Robert Daigneau.

The previous posts were:


The Web Service Evolution patterns talk about what to do to make the API evolve with the minimum breaking changes. What that means? It means that changing your API should not break clients that consume it, otherwise said, that your API is backward compatible.

From this chapter, three patterns apply to the Rails framework:

  • Tolerant Reader
  • Consumer driven contract
  • Versioning

Tolerant Reader refers to the client. It means that if you write a client, you should be "tolerant" on what you get as a response. Rails provides the ActiveResource object. This object will get the response and create an object from it. This object per se is very tolerant.

However, only using that object does not make you tolerant reader. ActiveResource will create a model object like ActiveRecord does. For example, you can have a Product object that inherits from ActiveResource, the same way you would with ActiveRecord

  class Product < ActiveResource
  end

ActiveResource will create an attribute for each attribute found in the response. Thus if you expect a "name" attribute, you would try:

  Product.find(1).name

However, what if name is not in the response? Then, if you really want to be tolerant, you should do

  p = Product.find(1)
  if p.respond_to?(:name)
    p.name
  end

Thus, with little effort you can have a tolerant reader, even thought Rails per se does not fully implement it.
 
Consumer driven contract means testing. Actually, it means having a test suite provided by your consumer on what it expects from the service. Testing is a common good practice in Rails and Rails provides you with a lot of resources for that (rspec, factory girl, stubs, mock ups). You only need to use them to specify what you expect from the API, or what a consumer should expect, depending on whether you are writing the producer or the consumer.

And finally, versioning. Versioning is a very good practice for an API. How you can implement versioning is very well explained on this rails cast, better than I could do myself, so I'd recommend you watching it, to see how to use namespaces in the routes file and in the controller implementation:

 
 
Web Service Evolution patterns are partly implemented on Rails. However, it needs you to do some implementation in order to follow them, because the typical example (the one you would generate with "rails generate") does not implement them, but give you the tools or the base so that you can do it.
 
For example, versioning is not implemented when you generate a new rails application, but you can easily do it with namespacing; tests are not implemented but rails gives you the tools to do so; tolerant reader can be implemented by using ActiveResource and respond_to? method.

Thus, my conclusion is that Rails, even thought it is very cool for simple applications since "rails generate" can give you what you need right away, when it comes to more serious things, you need to do some work yourself.
 
This means that, despite the fact that when you start using rails for simple things you don't need to know about patterns, if you want to do more, you need to know them.



No comments: