2

I work on an open source project - object document mapper aka. ODM in javascript and am facing a design decision I struggle with (rather simple but not easy) .

In the ODM you can define a Model object which describes your data document in a database. When creating a new Model instance object you can pass in it's data values via an argument.
Pseudocode:

var data = {
    username: 'james'
    email: 'james@email.com',
    country: {
        code: 'US',
        city: ''
    } 
};
// In case the data object would not be cloned, it would be mutated by the ODM
var user = new UserModel(data);

Now, the decision I'm facing is whether to automatically clone data object in the Model before it's processed by the application (ODM). I incline to a choice of not cloning the input data. I feel like that in the javascript community it's quite popular to clone data usually more than it's necessary because it can make things easy (not simple) for end-users. From my research I've found out that some similar and popular projects made a decision to clone the data, yet I'm not sure that's the right choice considering a Model can have large & complex data schema and while it does not make any difference in case of tens of Model instance objects, I think it could be an issue while working with eg.: hundreds of Model objects.

I'd love to hear your reasoning on the topic and what would be your choise?
In case you already see another answer, please, don't hesitate to write down your thoughts!
Thank you!

user2694295
  • 366
  • 6
  • 16

1 Answers1

0

If you keep using the input object to hold the entity's data, you will face a problem like the following if you don't clone the object:

var UserModel = function(data) {
  this.data = data;
}
UserModel.prototype.getUsername = function() {
  return this.data.username;
}

var data = {username: 'james'};
var james = new UserModel(data);

data.username = 'john';
var john = new UserModel(data);

console.log(james.getUsername()); // "john"
console.log(john.getUsername());  // "john"

This may lead to some nasty bugs that may not always be obvious.


Instead of cloning, the cleanest approach in my opinion would be to instead extract the relevant info out of the object and keep it as member properties directly:

var UserModel = function(data) {
  this.username = data.username;
  this.email = data.email;
}
UserModel.prototype.getUsername = function() {
  return this.username;
}
UserModel.prototype.getEmail = function() {
  return this.email;
}

Even better, explicitly declare the properties as arguments of the constructor:

var UserModel = function(username, email) {
  this.username = username;
  this.email = email;
}
TimoStaudinger
  • 41,396
  • 16
  • 88
  • 94
  • Thank you for your input @Timo.In your proposed design approaches you have not avoided causing the same issue you've described, considering data properties are not just data primitives but also nested objects / arrays. I consider it the mistake of mine by providing too much simple code example. About the issue you described - what I'd like understand is why would one consider using an Object in that way without cloning it first before it's passed to another model.Shouldn't we treat Objects / Arrays as references to memory by default instead of assuming it's handled the same way a primitive is? – user2694295 Dec 08 '16 at 20:17
  • 1
    Dealing not only with primitives, the same principle applies and copying may be necessary unless you structure child objects into separate entities. In a perfect world my first example would never happen. However, it usually is better to take the gun away from your (API's) user rather than to allow him to shoot himself in the foot and to tell him that he should have known better afterwards. The user of your API doesn't necessarily know anything about the implementation details, and neither should he have to. The more assumptions you make, the harder it will be to use the API you are designing. – TimoStaudinger Dec 08 '16 at 20:25