So this is a generic pattern question but one I have been going back and forth with for some time.
Should a model have a save method in MV*?
I often jump back and forth between Knockout, Ember, and sometimes even Angular but one of the persistent questions I always have is should the model have a save method on it's class or prototype that knows how to save changes to reduce dependencies around the application on services (aka the model has a service for saving that all of the other view models / controllers inherit by knowing about the model) or should there be a service that each of the view models / controllers depend on that has a specific method for saving changes to the object?
Example JavaScript pseudo-code
var person = new Model.Person();
person.name = 'Bill';
person.save();
vs
var personService = require('services/person.service');
var person = new Model.Person();
person.name = 'Bill';
personService.save(person);
Both accomplish the same purpose of saving the person but in Example 1 the view model / controller doesn't know about the service or how it is implemented only that if you want to change a person, you save it. In example two obviously we have to know about not only the way to save it but how the save is implemented.
I realize that this is an opinion-based question but if you can back up your opinion with facts it will be factual, so please have references to back up any claims so that this is not closed as 'Primarily opinion based'
It depends on the pattern (and not at all opinion based imo).
Your first example: A domain object having a .save
method is called an ActiveRecord (also see here).
Your second example: A mapper between the data mapping and domain layers is called a Repository (also see here)
Quoting Fowler:
An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.
The ActiveRecord pattern generally excels at prototyping, and is a good idea sometimes in very small applications where there exists a 1-1 mapping between objects and DB rows. Generally, you want to separate the logic of persisting an object and the logic of an actual domain object, since they are inherently different responsibilities.
This is one of the simplest ways to logically handle the persistence of data.
For example, this is what Backbone models and collections do with their sync()
method. Which causes them to persist to the server. It's often the reason you see larger Backbone applications not use sync()
altogether in favor of implementing their own adapters. After all, in the Backbone world it forces a 1-1 mapping between your REST API and your domain objects, effectively making your domain objects and data transfer objects the same which can grow hard to maintain as your application grows.
Quoting Fowler again:
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
A repository is generally a better design pattern for larger applications since it removes the persistence logic from your domain object, so it does better separation of concerns.
Implementation wise, a repository usually looks like this:
However, to its users a repository might look like:
As any abstraction, another object for a responsibility has some overhead, however - as an app grows it starts to pay off. If you create a $resource
with Angular and wrap it in a service that maps these objects from the db query to your domain objects (your data mapper) and then query that service like a collection - that's a repository for you.
The short answer in my personal opinion would be your second version. I typically think that the viewmodel on the client is basically a bag of data or properties. Services are used to persist/get those viewmodels to/from the server, and the controller is responsible for consuming the service methods and rendering the view. In the Angular.js world directives would handle any DOM-specific behavior, but I think that ultimately the events that happen as a result of UI interaction (saving, validation, etc.) would be handled by the controller (perhaps by calling a service method).
I formulated this opinion when I started using Angular.js, and was going through their developer documentation. The short of it is:
Use controllers to:
Do not use controllers to:
Use services to organize and share code and state across your app.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With