Tuesday, December 23, 2014

Service Patterns - Simple is Better

I have experimented with a variety of patterns for persistence services in Angular. Invariably, these require us to GET or POST (among others) to a RESTful endpoint.

I'm a huge fan of dto's and ui-side models. I like to define my models in an Angular .factory, and include all the UI-side logic in the model. They are very easy to test.

By design, a service is a singleton (using Angular .service). It consumes and emits models. Simple ... I like simple. By what patterns and providers should you use for your service?

I Don't Like ngResource

When I first started with Angular, ngResource was a seductive option for interfacing with a RESTful endpoint.  Minimal code.  But that's about it for me!   

I like a separation between the service and the model.  ngResource melds the 2.  You add your model logic to the service ... but it doesn't make it easy to test!

The biggest dis-likes of ngResource:
  1. It's polymorphic.  You could get a singleton object, you could get an array.
  2. It's not strongly typed.  I like to test to see if an account an instance of Account.  ngResource only produces "Resource" objects.  I like to validate types in key functions.  Strong types are easier to debug, as Chrome tells you what your object is.
That said, if you want a quick and dirty, small system with a very few endpoints, ngResource makes things easy.

Back to $http

So I've settled on writing close to the metal, using $http.

My standard pattern is ..
  1. Form url.
  2. Transform object (if posting) to dto.
  3. Wrap $http in a promise.
  4. Return the promise.
  5. Have $http success and failure functions where the promise is resolved or rejected.  The response is transformed from dto to my view-model in the success function, not the transformRequest parameter. (Why? -- see Comments)

Comments


  1. Some people like the way ngResource manages urls and query arguments .. and it is clean. This is a pattern for 100% control.
  2. This decouples us from the server-side object.
  3. Why do I  wrap the $http promise in another promise?  Again, control.  I can decide to absorb certain errors, and resolve successfully back to my calling function.  I use this technique for authentication.  The fact a request was bounced back because I am not authenticated is an error (from the server's POV), but just a message on the UI.
  4. The promise returned, and the contents are now all under our control.
  5. We resolve returned objects and transform from dto's to our view models.  Why not use transformResponse?  If you get an error, you're stuck in the transform function, and don't have access to the status code and headers.  I only want to transform if the server tells me the response is OK (200).
Simple.  Do you like it?

No comments:

Post a Comment