Why REST is Better – Part 1 – Explained in Code

Share the article!

When I was studying Physics the quickest way to end an argument was to show the explanation in mathematics (albeit a lot of handwaving mathematics!). Most software developers on the otherhand do not grok math, however they surely do grok code. Therefore if you could explain your arguments through code then you would have improved your odds of getting your message through.

A lot of developers also have a tendency to embrace unnecessary complexity. Deeply embedded in the psychology is the belief that complex problems require complex solutions. However, I have two general principle that debunk the deeply ingrained belief. The first principles is “Occam’s Razor.” Which proposes that the explanation requiring the fewest assumptions is most likely to be correct. The principle is often called the Principle of Parsimony.

The second principle is from Stephen Wolfram who explains in his book “A New Kind of Science” that very simple computational systems often achieve great complexity. In otherwords, the explanation of a complex (possibly irredicable) system can be attributed to simple computational constructs.

Many SOAP adherents make the claim that systems are complex and therefore require complex solutions. REST is too simple and therefore cannot be used to solve complex problems. This claim is same claim that any vendor will make to help you justify paying a higher margin. The reality is, the claim is entirely false, complex systems can be built using simple components.

The key problem SOAP and REST are trying to solve is interoperability, don’t let anyone distract you from this. Like the realtors say, the 3 most important things about choosing SOAP over REST is “Interoperability, Interoperability, Interoperability”.

So let’s begin the explanation of why REST is better than SOAP in its support of interoperability. Let’s start with a simple method invocation (java syntax):

object.method( arg1, arg2, arg3 );

A collection of these methods is the typical starting point of a SOAP implementation. The first interoperability problem that we encounter here is the order of the arguments. A better solution would be to introduce names, optional, default arguments. In the absence of this construct the way to do this is to objectify all the arguments (see Half-Bean Pattern). So now we have this:

document.arg1 = a;
document.arg3 = c;
document.arg2 = null;
object.method( document );

The advantage is that there is no explicit dependencies in the order of the arguments. Second you have the opportunity to introduce optional arguments. Finally, there is no need to have multiple method signatures for different calling variations. This allows one to design the API to make the most common operations easy to do.

Now that we simplified the arguments, let’s now look at a set of these methods packaged in an interface:

/* code snippet 1 */
interface Interface {
    Result1 method1( Request1 req );
    Result2 method2( Request2 req );
    Result3 method3( Request3 req );
    
}

Here again there is a sequencing problem. Objects contain state and therefore the order you invoke a method can affect its behavior. That is:

i.method1 ( req1 );
i.method2 ( req2 );
i.method3 ( req3 );

Differs from:

i.method2 ( req2 );
i.method1 ( req1 );
i.method3 ( req3 );

To avoid this problem, we try to consolidate all method to avoid unnecessary sequencing dependencies. We refactor this to:

interface Protocol {
    Result1 method1( Interface i, Request1 req );
    Result2 method2( Interface i, Request2 req );
    Result3 method3( Interface i, Request3 req );
    
}

Refactoring it further using the object as arguments approach, we have:

interface Protocol {
    Result1 method1( IRequest1 req );
    Result2 method2( IRequest2 req );
    Result3 method3( IRequest3 req );
    
}

That is, IRequest1.i contains the object instance i and IRequest1.req contains the object instance req. We can do the refactoring further by parameterizing the method:

interface Protocol {
    Response invoke( MethodKind method, Request req );
}

Where Response and Request are generalized objects that contains (see Adaptive Object Model). Finally, simplify it further by containing MethodKind in request:

/* code snippet 2 */
interface Protocol {
    Response invoke( Request req );
}

Java practitioners will recognize this as the DTO pattern. The benefit of which is to reduce the number of round trips between interacting systems. However, the side-effect is that it forces the designer to structure interfaces as to remove extraneous state transitions.

In the REST world we standardize on a few verbs, so the result would look more like this:

interface Protocol {
    Response get( Request req );
    Response put( Request req );
    Response post( Request req );
    Response delete( Request req );
}

All these refactorings preserve behavior, all we have done is continually moved domain specific deeper into the object hierarchy so that we end up with a generic interface that’s independent of domain. Functionally it’s the same, however stucturally its different. The structure is what provides support for interoperability. (Note: There’s one functional difference, the original has more observed states as the later, this constraint however is in fact beneficial to interoperability).

To achieve interoperability you need constucts that resolve incompatibility problems. In the Design Patterns we have the Adapter pattern and the Decorator Pattern to dynamically modify behavior (see the Holy Structures. One could look at the Decorator pattern as a specialization of the Adapter pattern, however it has a particular feature. That is, you can chain Decorators together, dynamically composing functionality. With Adapter its possible but its not as easy.

So if we needed an adapter for code snippet 1, we would need to create a new class that does the adaption from one interface to another:

class Adapter {
   Interface i; 
   NewResult1 newmethod1( NewRequest1 req )
       {
          return convertResponse(i.method1( convertRequest( req ) ));
       };
    Request1 convertRequest( NewRequest1 ) {...};
    NewResult1 convertResponse( Response1 ) {...};
    ... repeat for every method ... 
}

The Decorator equivalent applied to code snippet 2 would be:

class Decorator implements Protocol {
      Protocol c;
      Response invoke( Request req )
         {
         convertResponse( c.invoke( convertRequest(req) ); 
         };
      Request convertRequest( Request ) {...};
      Response convertResponse( Response ) {...}; 
}

These are several advantages for Decorators using uniform interfaces:

  • You have less methods to write.
  • Reusablility is easier since you don’t have to adapt an existing adapter to work in a different context.
  • Invocation sequencing has been removed, therefore you don’t have the difficult problem of adapting one statemachine into another.

I’ve run out of time for now, however I’ll show at a later date how asynchronous methods play favorably in the interoperability equation and I will show why pushing semantics deeper down the object hierarchy actually makes perfect sense. See part 2.


Share the article!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>