[ovirt-devel] oVirt.js - API & implementation design summary

Vojtech Szocs vszocs at redhat.com
Tue Jun 17 14:05:03 UTC 2014


Hello,

this is a follow-up to my oVirt.js announcement email, I'd like to give
you a quick summary of API & implementation design. In other words, I'll
try to describe key decisions in oVirt.js API & implementation.

1, General design

We're trying to follow best practices in JavaScript development, such as:

- containing the whole SDK in a single global variable (because until ES6
  we cannot truly avoid global variables) and using IIFE/closure for better
  information hiding

- using ES5 "use strict" (Strict Mode) on function level (so that Strict
  Mode compliant environments will detect problems early) while not relying
  on semantical differences introduced by Strict Mode (main reason = IE9)

2, API design

As presented in earlier "oVirt JavaScript SDK: Design Proposal" session,
our main goals here are API usability and readability. (If you missed
this session before, let me know and I'll forward you the slides.)

- using fluent interface concept, for example:
  ovirt.api.datacenters.list().success(dcsListedCallback).run();

- separating the concept of resource (collection) vs. operation
  (operation = reusable RESTful method abstraction)

- separating three logical namespaces:

  * ovirt.svc -- services used by API implementation
    Each service should be an object containing reusable methods.
      1. existing services can be overridden to achieve SDK portability
         for given runtime environment.
      2. existing services can be used and new services can be added by
         SDK extensions or clients.

  * ovirt.util -- utilities used by API implementation
    Unlike services, utilities are meant to assist with writing more
    efficient JavaScript code.

  * ovirt.api -- API as the main entry point to SDK functionality

3, Implementation design

I've tried to catch up with my JavaScript knowledge recently, but I'm
not an expert, I'm still learning things. Following are ideas which I
took into practice within oVirt.js API implementation:

- creating objects via mixins, each mixin is a function which gets some
  object ("that") and adds new properties to that object (augmentation)

- simple dependency injection mechanism for mixins, with dependencies
  resolved against "ovirt.svc" namespace, for example:

  // Create new object, "spec" = information needed for object creation.
  function makeSomeObject (spec) {
    return mixins.mix(spec,
        [mixinOne, mixinTwo], // array of mixin functions
        ['depNameOne', 'depNameTwo'] // array of dependency names
    );
  }

  // "that" = object to augment.
  // "spec" = information provided for object creation.
  // "my" = object containing shared secrets, for more details see [1]
  function mixinOne (that, spec, my) {
    // Augment "that" with "quack" method, utilize closure to capture
    // "spec.name" without exposing it to users of "that" object.
    that.quack = function () { console.log(spec.name + ' says quack!');
    // Consume "depNameOne" service, resolved from ovirt.svc namespace
    that.doSomething = function () { my.depNameOne.whatever(); }
  }

[1] JavaScript: The Good Parts (2008) by Douglas Crockford,
    chapter "Inheritance", section "Functional" - explanation
    of "my" object within functional constructor pattern

We are NOT using "new" operator in JavaScript. We are also NOT relying
on object's prototype chain. We are simply creating useful objects via
mixins, which is in my opinion the most simple and flexible way.

Regards,
Vojtech



More information about the Devel mailing list