4. Write the Model Code

4.1. Encode the add and remove operations

For the multi-valued reference property Book::authors, we need to encode the operations addAuthor and removeAuthor. Both operations accept one parameter denoting an author either by ID reference (the author ID as integer or string) or by an internal object reference. The code of addAuthor is as follows:

Book.prototype.addAuthor = function (a) {
  var constraintViolation=null,
      authorIdRef=0, authorIdRefStr="";
  // an author can be given as ...
  if (typeof( a) !== "object") {  // an ID reference or
    authorIdRef = parseInt( a);
  } else {                       // an object reference
    authorIdRef = a.authorId;
  }
  constraintViolation = Book.checkAuthor( authorIdRef);
  if (authorIdRef && 
      constraintViolation instanceof NoConstraintViolation) {
    // add the new author reference
    authorIdRefStr = String( authorIdRef);
    this.authors[ authorIdRefStr] = 
        Author.instances[ authorIdRefStr];
  }
};

The code of removeAuthor is similar to addAuthor:

Book.prototype.removeAuthor = function (a) {
  var constraintViolation = null;
  var authorIdRef = "";
  // an author can be given as ID reference or object reference
  if (typeof(a) !== "object") authorIdRef = parseInt( a);
  else authorIdRef = a.authorId;
  constraintViolation = Book.checkAuthor( authorIdRef);
  if (constraintViolation instanceof NoConstraintViolation) {
    // delete the author reference
    delete this.authors[ authorIdRef];
  }
};

For assigning an array of ID references, or a map of object references, to the property Book::authors, the method setAuthors adds them one by one with the help of addAuthor:

Book.prototype.setAuthors = function (a) {
  var keys=[];
  this.authors = {};
  if (Array.isArray(a)) {  // array of IdRefs
    for (i= 0; i < a.length; i++) {
      this.addAuthor( a[i]);
    }
  } else {  // map of object references
    keys = Object.keys( a);
    for (i=0; i < keys.length; i++) {
      this.addAuthor( a[keys[i]]);
    }
  }
};

4.2. Implement a deletion policy

For the reference property Book::authors, we have to implement a deletion policy in the destroy method of the Author class. We have to choose between

  1. deleting all books (co-)authored by the deleted author;

  2. dropping from all books (co-)authored by the deleted author the reference to the deleted author.

We go for the second option. This is shown in the following code of the Author.destroy method where for all concerned book objects book the author reference book.authors[authorKey] is dropped:

Author.destroy = function (id) {
  var authorKey = id.toString(),
      author = Author.instances[authorKey],
      key="", keys=[], book=null;
  // delete all dependent book records
  keys = Object.keys( Book.instances);
  for (i=0; i < keys.length; i++) {
    key = keys[i];
    book = Book.instances[key];
    if (book.authors[authorKey]) delete book.authors[authorKey];
  }
  // delete the author record
  delete Author.instances[authorKey];
  console.log("Author " + author.name + " deleted.");
};

4.3. Serialization and De-Serialization

The serialization method convertObj2Row converts typed objects with internal object references to corresponding (untyped) record objects with ID references:

Book.prototype.convertObj2Row = function () {
  var bookRow = util.cloneObject(this), keys=[];
  // create authors ID references
  bookRow.authorsIdRef = [];
  keys = Object.keys( this.authors);
  for (i=0; i < keys.length; i++) {
    bookRow.authorsIdRef.push( parseInt( keys[i]));
  }
  if (this.publisher) {
    // create publisher ID reference
    bookRow.publisherIdRef = this.publisher.name;
  }
  return bookRow;
};

The de-serialization method convertRow2Obj converts (untyped) record objects with ID references to corresponding typed objects with internal object references:

Book.convertRow2Obj = function (bookRow) {
  var book=null, authorKey="",
      publisher = Publisher.instances[bookRow.publisherIdRef];
  // replace the "authorsIdRef" array of ID references
  // with a map "authors" of object references
  bookRow.authors = {};
  for (i=0; i < bookRow.authorsIdRef.length; i++) {
    authorKey = bookRow.authorsIdRef[i].toString();
    bookRow.authors[authorKey] = Author.instances[authorKey];
  }
  delete bookRow.authorsIdRef;
  // replace publisher ID reference with object reference
  delete bookRow.publisherIdRef;
  bookRow.publisher = publisher;

  try {
    book = new Book( bookRow);
  } catch (e) {
    console.log( e.constructor.name + 
        " while deserializing a book row: " + e.message);
  }
  return book;
};