base-domain
Version:
simple module to help build Domain-Driven Design
385 lines (292 loc) • 8.48 kB
JavaScript
var Facade, camelize, copy, getProto, ref, ref1, requireFile,
slice = [].slice;
require('es6-promise').polyfill();
copy = require('copy-class').copy;
ref = require('./util'), camelize = ref.camelize, requireFile = ref.requireFile;
getProto = (ref1 = Object.getPrototypeOf) != null ? ref1 : function(obj) {
return obj.__proto__;
};
/**
Facade class of DDD pattern.
- create instance of factories
- create instance of repositories
@class Facade
@module base-domain
*/
Facade = (function() {
/**
create instance of Facade
@method createInstance
@static
@param {Object} [options]
@return {Facade}
*/
Facade.createInstance = function(options) {
var Constructor;
if (options == null) {
options = {};
}
Constructor = this;
return new Constructor(options);
};
/**
constructor
@constructor
@param {String} [options]
@param {String} [options.dirname="."] path where domain definition files are included
*/
function Facade(options) {
var ref2;
this.classes = {};
this.dirname = (ref2 = options.dirname) != null ? ref2 : '.';
this.init();
}
Facade.prototype.init = function() {};
/**
load master tables
@method loadMasterTables
@return {Promise}
*/
Facade.prototype.loadMasterTables = function() {
var modelName, modelNames;
modelNames = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return Promise.all((function() {
var base, i, len, results;
results = [];
for (i = 0, len = modelNames.length; i < len; i++) {
modelName = modelNames[i];
results.push(typeof (base = this.getRepository(modelName)).load === "function" ? base.load() : void 0);
}
return results;
}).call(this));
};
/**
get a model class
@method getModel
@param {String} name
@return {Class}
*/
Facade.prototype.getModel = function(name) {
return this.require(name);
};
/**
get a factory class
ISSUE: user will never know load failure
@method getFactory
@param {String} name
@param {Boolean} [useAnonymousWhenFailed=false]
@return {Function}
*/
Facade.prototype.getFactory = function(name, useAnonymousWhenFailed) {
var AnonymousFactory, e;
if (useAnonymousWhenFailed == null) {
useAnonymousWhenFailed = false;
}
try {
return this.require(name + "-factory");
} catch (_error) {
e = _error;
if (!useAnonymousWhenFailed) {
throw e;
}
AnonymousFactory = Facade.BaseFactory.getAnonymousClass(name);
return this.addClass(name + "-factory", AnonymousFactory, true);
}
};
/**
get a repository class
@method getRepository
@param {String} name
@return {Class}
*/
Facade.prototype.getRepository = function(name) {
return this.require(name + "-repository");
};
/**
create a factory instance
@method createFactory
@param {String} name
@param {Boolean} [useAnonymousWhenFailed=false]
@return {BaseFactory}
*/
Facade.prototype.createFactory = function(name, useAnonymousWhenFailed) {
var FactoryClass;
if (useAnonymousWhenFailed == null) {
useAnonymousWhenFailed = false;
}
FactoryClass = this.getFactory(name, useAnonymousWhenFailed);
return new FactoryClass();
};
/**
create a repository instance
@method createRepository
@param {String} name
@param {Object} [options]
@return {BaseRepository}
*/
Facade.prototype.createRepository = function(name, options) {
return this.create(name + "-repository", options);
};
/**
read a file and returns class
@method require
@private
@param {String} name
@return {Function}
*/
Facade.prototype.require = function(name) {
var file, klass;
if (this.classes[name] != null) {
return this.classes[name];
}
file = this.dirname + "/" + name;
klass = requireFile(file);
return this.addClass(name, klass);
};
/**
check existence of the class of the given name
@method hasClass
@param {String} name
@return {Function}
*/
Facade.prototype.hasClass = function(name) {
var e;
try {
this.require(name);
return true;
} catch (_error) {
e = _error;
return false;
}
};
/**
add copied class to facade.
the class is acquired by @require(name)
attaches getFacade() method (for both class and instance)
@method addClass
@private
@param {String} name
@param {Function} klass
@param {Boolean} skipNameValidation validate class name is compatible with the name to register
@return {Function}
*/
Facade.prototype.addClass = function(name, klass, skipNameValidation) {
var Class, CopiedParentClass, ParentClass, camelCasedName, facade;
if (skipNameValidation == null) {
skipNameValidation = false;
}
if (skipNameValidation) {
camelCasedName = camelize(name);
} else {
if (klass.getName() !== name) {
throw this.error("given class should be named '" + (klass.getName()) + "',\nbut '" + name + "' given.");
}
camelCasedName = klass.name;
}
ParentClass = getProto(klass.prototype).constructor;
if (this.constructor.isBaseClass(ParentClass)) {
Class = copy(klass, camelCasedName);
} else {
CopiedParentClass = this.require(ParentClass.getName());
Class = copy(klass, camelCasedName, CopiedParentClass);
}
facade = this;
Class.getFacade = function() {
return facade;
};
Class.prototype.getFacade = function() {
return facade;
};
return this.classes[name] = Class;
};
/**
read a file and returns the instance of the file's class
@method create
@private
@param {String} name
@param {Object} [options]
@return {BaseFactory}
*/
Facade.prototype.create = function(name, options) {
var DomainClass;
DomainClass = this.require(name);
return new DomainClass(options);
};
/**
create instance of DomainError
@method error
@param {String} reason reason of the error
@param {String} [message]
@return {Error}
*/
Facade.prototype.error = function(reason, message) {
var DomainError;
DomainError = this.constructor.DomainError;
return new DomainError(reason, message);
};
/**
check if given object is instance of DomainError
@method isDomainError
@param {Error} e
@return {Boolean}
*/
Facade.prototype.isDomainError = function(e) {
var DomainError;
DomainError = this.constructor.DomainError;
return e instanceof DomainError;
};
/**
insert fixture data
(Node.js only)
@method insertFixtures
@param {Object} [options]
@param {String} [options.dataDir='./data'] directory to have fixture data files
@param {String} [options.tsvDir='./tsv'] directory to have TSV files
@param {Array(String)} [options.models=null] model names to insert. default: all models
@return {Promise(Object)} dataPool inserted data
*/
Facade.prototype.insertFixtures = function(options) {
var Fixture, fixture;
if (options == null) {
options = {};
}
Fixture = require('./fixture');
fixture = new Fixture(this, options);
return fixture.insert(options.models).then(function() {
return fixture.dataPool;
});
};
/**
check the given class is registered in facade
@method isBaseClass
@static
@param {Function} klass
@return {Boolean}
*/
Facade.isBaseClass = function(klass) {
var ref2;
return (klass === this[klass.name]) || (klass === this.DomainError) || (((ref2 = this[klass.name]) != null ? ref2.toString() : void 0) === klass.toString());
};
/**
registers the given class as a base class
@method registerBaseClass
@static
@param {Function} klass
*/
Facade.registerBaseClass = function(klass) {
return this[klass.name] = klass;
};
Facade.Base = require('./base');
Facade.BaseModel = require('./base-model');
Facade.Entity = require('./entity');
Facade.BaseList = require('./base-list');
Facade.BaseDict = require('./base-dict');
Facade.BaseFactory = require('./base-factory');
Facade.ListFactory = require('./list-factory');
Facade.DictFactory = require('./dict-factory');
Facade.BaseRepository = require('./base-repository');
Facade.MasterRepository = require('./master-repository');
Facade.DomainError = require('./domain-error');
return Facade;
})();
module.exports = Facade;