sábado, 7 de marzo de 2015

The Isomorphic Response Reference Pattern based on MongoDB using Dart

I am relatively new to server-side web programming, and I am enjoying it: I am coding a server in Dart using MongoDart and the fantastic Redstone Dart framework. I recently remade all may server code because I discovered this great video by Les Hazlewood about REST API's


The thing that really shocked me about this video (well one of them, I was really searching for security and authentication) was a pattern shown here to create references between "server" entities.

Normally when you need to create reference between entities, you just store the database id as a string or int of the other object, and you end up writing classes like

 class A   
  {   
   String idB;   
   B b;   
  }   

A very intuitive approach, but it creates duplicate fields on A, and since B also has its own id field then it creates redundant information. To avoid this you can use what I'll call the Reference Pattern and as a class can be represented (in Dart) as

 abstract class Ref extends DbObj
 {  
   String get href;  
 }
where DbObj is
 class DbObj extends Resp  
 {  
   String id;  
 }  
The idea is that in JSON a Ref this looks something like
 {   
   "id" : "someId"  
   "href" : "http://someHost/someResource/{id}   
  }  
Now, if you make both A and B inherit from Ref then, they they could be defined as

  class A extends Ref   
  {   
   String someField;  
   B b;   
   String get href => "http://someHost/A/$id";  
  }   
  class B extends Ref   
  {   
   String someOtherField;  
   String get href => "http://someHost/B/$id";  
  }   

Where is the magic? The important thing is that when you store an A object on MongoDB, remember to only store a blank B object inside that only contains its own id field. Now you can retrieve it, encode it, and the client can receive it as a JSON like this

 {  
   "someField" : "whatever",  
   "b" : {  
     "id" : "id_of_B",  
     "href" : "http://someHost/B/id_of_B"  
   },  
   "id" : "id_of_A",  
   "href" : "http://someHost/A/id_of_A"  
 }  
}

Notice 2 things:

  1. You can plug the href's directly into a request, src tag, or whatever, making it super easy to get, put, or delete the entity.
  2. You can expand "b" to get the full object after making a get request to the its "href" and store it back on "b" again. Also, you can send a partial or complete B object from the beginning without have to create an extra structure to compensate.
Now you can receive a JSON like this with no problems

 {  
   "someField" : "whatever",  
   "b" : {  
     "id" : "id_of_B",  
     "href" : "http://someHost/B/id_of_B",  
     "someOtherField" : "Nice!"  
   },  
   "id" : "id_of_A",  
   "href" : "http://someHost/A/id_of_A"
 }  

They key is: all your structures inherit from `Ref` so they all contain the minimal data needed to retrieve/modify them, but they can be expanded. I other words, your object are isomorphic!

If you've read carefully you'd notice that the title takes about a "Response" and there is an undefined "Resp" class. This Resp class have the purpose of encapsulating the possibility of all requests to the server: failure. Ok, you can let the server crash and send back a 4xx or 5xx status, then on the client you have to catch that error on the request, but things might get messy. Another option is to send back a JSON with the error, something like

 {"error" : "not found"}  

then decode the JSON to an object and just check for the error. Here is the implementation of the Resp class

 class Resp  
 {  
   bool get success => nullOrEmpty(error);  
   bool get failed => ! success;  
   String error;  
 }  

Since Ref extends DbObj which extends Resp, all your objects can also warn you about errors and you can write beautiful code like this (which also uses Darts async/await syntax)


 A a = await getA ();  
 a.b = await getRequest (a.b.href); 
 if (a.b.failed)  
 {  
   print (a.b.error);  
   return;  
 }
 //Do something with a.b


No hay comentarios:

Publicar un comentario