Sunday, April 22, 2012

MVVM and Dependencies

I am still learning Model-View-ViewModel via KnockoutJS. One concern I find myself continually coming back to is handling dependency. Let me illustrate with a simple example.

   1:  function PersonViewModel(personModel){
   2:      this.Name = ko.observable(personModel.Name);
   3:  }

Here we have created a simple view model for a person – not concerned with dependencies just yet… but now we want to be able to save a person – so we end up with…


   1:  function PersonViewModel(personModel){
   2:      this.Name = ko.observable(personModel.Name);
   3:      this.save = function(){
   4:          $.ajax(...);
   5:      };
   6:  }



This is a point of contention. My sniffer goes off about Single Responsibility, it is very possible that at some point we may change how we access the server for Person, and Person should be unaffected (ie. url change, moving some arguments into the url/ query string, switching from rest to node.js, or not hit the server at all, like local storage). So lets get some separation of concerns in here.


   1:  function PersonViewModel(personModel, personDataAccessObject){
   2:      var self = this;
   3:      self.Name = ko.observable(personModel.Name);
   4:      self.save = function(){
   5:          personDataAccessObject.save(self);
   6:      };
   7:  }



Alright, so this is looking good… until we start using it…


   1:  function ContactManagerViewModel(){
   2:      var self = this;
   3:      self.contacts = ko.observableArray([]);
   4:      self.createContact = function(){
   5:          self.contacts.push(new PersonViewModel({
   6:              Name: 'Sally'
   7:          }));
   8:      };
   9:  }



This is what we would like to see – note the simple call to construct a PersonViewModel – but this is going to fail miserably when we try to save (b/c we never passed in the personDataAccessObject). Surely we could just pass it in, but the coupling can get out of control as everything it needs, we need in our constructor, and we have to pass to it, and anytime its constructor changes so do we. Been there, done that, ain’t going back.


Options



  1. Global Namespace we can put personDataAccessObject in the global namespace so that any other class can get to it, it also provides a way for a consumer to change that dependency without effecting other code. What I don’ like is that because it is global – if there were multiple instances of our contactManager, it would be very difficult to allow them to use different implementations of personDataAccessObject because they are both pointing to the same global reference. (Note while on the surface personDataAccesssObject might not seem like a good example, imagine one instance is for preview and you don’t want to actually save data)

  2. Closure this style is going to be harder to explain, but basically the entire app (all the ViewModels, etc.) live in one closure so that they can share variables. This giant closure would have one public method exposed which returns the namespace so that you could put it wherever you want. Then you could have two of these namespaces, and can override the implementation of personDataAccessObject for just one of them. I prefer to keep my js classes encapsulated to one js file as this keeps me honest in terms of coupling. The equivalent of this in C# would be to use inner classes – which is rarely the right solution.

  3. Coupling to Parent  for our PersonViewModel, instead of taking a personDataAccessObject, we would take a ContactManagerViewModel and would reference it as parent. ContactManagerViewModel would have the responsibility of having a way for us to save. I don’t feel like this really solves anything. We still have coupling between the two and we still need to pass in extra stuff to PersonViewModel. Where I guess would buy us something is when PersonViewModel requires 5 dependencies, it could be just one.

  4. Factory Pattern in forcing myself to think about how I would solve this if I wasn’t in the UI – I land at Factory Pattern. The idea is that we would have one or more factories that are responsible for depdencies. They would encapsulate any instantiation logic so that there is one place that knows how to properly instantiate things, and would expose methods for things like personDataAccessObject.

Ironically, Factory Pattern is a little of the other 3 (but arguably the right little). Its constructor would live in the global namespace so that anyone can create one. It will have its own closure maintaining instances, etc. that is is not global like #2. And we will have to pass it to our children, but arguably only it, like #3.


So here is the factory


   1:  function ContactManagerFactory(){
   2:      this.getPersonDataAccessObject = function(){
   3:          return {
   4:              save : function(person){
   5:                  $.ajax(...);
   6:              }
   7:          };
   8:      }
   9:  }



we can new one up and override things at will (have as many separate instances as we want).


   1:   
   2:  var factory = new ContactManagerFactory();
   3:  factory.getPersonDataAccessObject = function(){
   4:      return {
   5:          save : function(){
   6:              // do nothing - this is  a preview
   7:          }
   8:      };
   9:  }




we can then pass it around like so


   1:  function ContactManagerViewModel(factory){
   2:      var self = this;
   3:      self.contacts = ko.observableArray([]);
   4:      self.createContact = function(){
   5:          self.contacts.push(factory.newPerson({
   6:              Name: 'Sally'
   7:          });
   8:      };
   9:  }



notice the need for a newPerson method (which needs to pass a factory to PersonViewModel and so happens to be a factory – see where this is going…)


   1:  function ContactManagerFactory(){
   2:      var self = this;
   3:      self.getPersonDataAccessObject = function(){
   4:          return {
   5:              save : function(person){
   6:                  $.ajax(...);
   7:              }
   8:          };
   9:      };
  10:   
  11:      self.newPerson = function(personModel){
  12:          return new PersonViewModel(personModel, self);
  13:      }
  14:  }



Lastly,


   1:  function PersonViewModel(personModel, factory){
   2:      var self = this;
   3:      self.Name = ko.observable(personModel.Name);
   4:      self.save = function(){
   5:          factory.getGersonDataAccessObject().save(self);
   6:      };
   7:  }



In closing, there are things I don’t like – (super factory object) but it’s the closest I was able to come up with, and the only thing I could come up with that I could stomach.

No comments:

Post a Comment