Edge Cases and Best Practices for Models
Handling Nullable Values
The BaseModel initializes undefined attributes with null on access:
javascript
const user = new User();
console.log(user.name); // null (not undefined)This makes property access more predictable and prevents undefined errors. It is a deliberate design choice to handle nested relations of objects and collections.
For example, calling order.items.add({}) on an empty Order model will ensure that order.items is a collection and calling add() on it won't generate an error.
Circular References
When dealing with models that reference each other, be careful with serialization:
javascript
// Potential circular reference problem
const user = new User({ name: 'John' });
const task = new Task({ title: 'Test', assignedTo: user });
// If we add a task reference back to the user
user._data.currentTask = task;
// This could cause a stack overflow during serialization
// To fix, add a custom serializer:
User.prototype._serialize_currentTask = function() {
// Return just the ID or simplified representation
return this._data.currentTask ? { id: this._data.currentTask.theId() } : null;
};Performance Considerations
For applications with many model instances:
- Minimize deep cloning: Use references when appropriate
- Batch updates: Use
setAttributes()instead of multiple individual property assignments - Avoid unnecessary getters/setters: For internal properties, under certain conditions you may access
_datadirectly in methods - Consider using
anyinstead ofstringas a type: This will omit the casting and serialization logic. Strings should be used only if you want to do string manipulation. - Consider lazy loading: For complex nested structures, load data on demand
Computed Properties
javascript
class Invoice extends BaseModel {
// ... getters and setters for items, taxRate, etc ...
get subtotal() {
return (this._data.items || []).reduce(
(sum, item) => sum + (item.price * item.quantity), 0
);
}
get taxAmount() {
return this.subtotal * (this._data.taxRate || 0) / 100;
}
get total() {
return this.subtotal + this.taxAmount;
}
}