UNPKG

eventric

Version:

Build JavaScript applications with Behaviour-driven Domain Design. Based on DDD, BDD, CQRS and EventSourcing.

247 lines (232 loc) 10.2 kB
var Projection, eventric, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; eventric = require('eventric'); Projection = (function() { function Projection() { this._applyDomainEventToProjection = __bind(this._applyDomainEventToProjection, this); this.log = eventric.log; this._handlerFunctions = {}; this._projectionInstances = {}; this._domainEventsApplied = {}; } Projection.prototype.initializeInstance = function(projectionObj, params, context) { return new Promise((function(_this) { return function(resolve, reject) { var ProjectionClass, aggregateId, diFn, diName, projection, projectionId, projectionName, _ref; projectionName = 'whoami'; if (projectionObj.name) { projectionName = projectionObj.name; } if (projectionObj["class"]) { ProjectionClass = projectionObj["class"]; projection = new ProjectionClass; } if (projectionObj.object) { projection = projectionObj.object; } if (context._di) { _ref = context._di; for (diName in _ref) { diFn = _ref[diName]; projection[diName] = diFn; } } projectionId = eventric.generateUid(); aggregateId = null; projection.$subscribeHandlersWithAggregateId = function(_aggregateId) { return aggregateId = _aggregateId; }; _this.log.debug("[" + context.name + "] Clearing Projections"); return _this._clearProjectionStores(projection.stores, projectionName, context).then(function() { _this.log.debug("[" + context.name + "] Finished clearing Projections"); return _this._injectStoresIntoProjection(projectionName, projection, context); }).then(function() { return _this._callInitializeOnProjection(projectionName, projection, params, context); }).then(function() { var eventName, eventNames, key, value; _this.log.debug("[" + context.name + "] Replaying DomainEvents against Projection " + projectionName); eventNames = []; for (key in projection) { value = projection[key]; if ((key.indexOf('handle')) === 0 && (typeof value === 'function')) { eventName = key.replace(/^handle/, ''); eventNames.push(eventName); } } return _this._applyDomainEventsFromStoreToProjection(projectionId, projection, eventNames, aggregateId, context); }).then(function(eventNames) { _this.log.debug("[" + context.name + "] Finished Replaying DomainEvents against Projection " + projectionName); return _this._subscribeProjectionToDomainEvents(projectionId, projectionName, projection, eventNames, aggregateId, context); }).then(function() { _this._projectionInstances[projectionId] = projection; context.publish("projection:" + projectionName + ":initialized", { id: projectionId, projection: projection }); return resolve(projectionId); })["catch"](function(err) { return reject(err); }); }; })(this)); }; Projection.prototype._callInitializeOnProjection = function(projectionName, projection, params, context) { return new Promise((function(_this) { return function(resolve, reject) { if (!projection.initialize) { _this.log.debug("[" + context.name + "] No initialize function on Projection " + projectionName + " given, skipping"); return resolve(projection); } _this.log.debug("[" + context.name + "] Calling initialize on Projection " + projectionName); return projection.initialize(params, function() { _this.log.debug("[" + context.name + "] Finished initialize call on Projection " + projectionName); return resolve(projection); }); }; })(this)); }; Projection.prototype._injectStoresIntoProjection = function(projectionName, projection, context) { return new Promise((function(_this) { return function(resolve, reject) { if (!projection.stores) { return resolve(); } if (projection["$store"] == null) { projection["$store"] = {}; } return eventric.eachSeries(projection.stores, function(projectionStoreName, next) { _this.log.debug("[" + context.name + "] Injecting ProjectionStore " + projectionStoreName + " into Projection " + projectionName); return context.getProjectionStore(projectionStoreName, projectionName, function(err, projectionStore) { if (projectionStore) { projection["$store"][projectionStoreName] = projectionStore; _this.log.debug("[" + context.name + "] Finished Injecting ProjectionStore " + projectionStoreName + " into Projection " + projectionName); return next(); } }); }, function(err) { if (err) { return reject(err); } return resolve(); }); }; })(this)); }; Projection.prototype._clearProjectionStores = function(projectionStores, projectionName, context) { return new Promise((function(_this) { return function(resolve, reject) { if (!projectionStores) { return resolve(); } return eventric.eachSeries(projectionStores, function(projectionStoreName, next) { _this.log.debug("[" + context.name + "] Clearing ProjectionStore " + projectionStoreName + " for " + projectionName); return context.clearProjectionStore(projectionStoreName, projectionName, function() { _this.log.debug("[" + context.name + "] Finished clearing ProjectionStore " + projectionStoreName + " for " + projectionName); return next(); }); }, function(err) { return resolve(); }); }; })(this)); }; Projection.prototype._applyDomainEventsFromStoreToProjection = function(projectionId, projection, eventNames, aggregateId, context) { return new Promise((function(_this) { return function(resolve, reject) { var findEvents; _this._domainEventsApplied[projectionId] = {}; if (aggregateId) { findEvents = context.findDomainEventsByNameAndAggregateId(eventNames, aggregateId); } else { findEvents = context.findDomainEventsByName(eventNames); } findEvents.then(function(domainEvents) { if (!domainEvents || domainEvents.length === 0) { return resolve(eventNames); } return eventric.eachSeries(domainEvents, function(domainEvent, next) { return _this._applyDomainEventToProjection(domainEvent, projection, function() { _this._domainEventsApplied[projectionId][domainEvent.id] = true; return next(); }); }, function(err) { if (err) { return reject(err); } return resolve(eventNames); }); }); return findEvents["catch"](function(err) { return reject(err); }); }; })(this)); }; Projection.prototype._subscribeProjectionToDomainEvents = function(projectionId, projectionName, projection, eventNames, aggregateId, context) { return new Promise((function(_this) { return function(resolve, reject) { var domainEventHandler, eventName, subscriberId, _base, _i, _len; domainEventHandler = function(domainEvent, done) { if (_this._domainEventsApplied[projectionId][domainEvent.id]) { return done(); } return _this._applyDomainEventToProjection(domainEvent, projection, function() { _this._domainEventsApplied[projectionId][domainEvent.id] = true; context.publish("projection:" + projectionName + ":changed", { id: projectionId, projection: projection }); return done(); }); }; for (_i = 0, _len = eventNames.length; _i < _len; _i++) { eventName = eventNames[_i]; if (aggregateId) { subscriberId = context.subscribeToDomainEventWithAggregateId(eventName, aggregateId, domainEventHandler, { isAsync: true }); } else { subscriberId = context.subscribeToDomainEvent(eventName, domainEventHandler, { isAsync: true }); } if ((_base = _this._handlerFunctions)[projectionId] == null) { _base[projectionId] = []; } _this._handlerFunctions[projectionId].push(subscriberId); } return resolve(); }; })(this)); }; Projection.prototype._applyDomainEventToProjection = function(domainEvent, projection, callback) { if (!projection["handle" + domainEvent.name]) { this.log.debug("Tried to apply DomainEvent '" + domainEvent.name + "' to Projection without a matching handle method"); return callback(); } if (projection["handle" + domainEvent.name].length === 2) { return projection["handle" + domainEvent.name](domainEvent, callback); } else { projection["handle" + domainEvent.name](domainEvent); return callback(); } }; Projection.prototype.getInstance = function(projectionId) { return this._projectionInstances[projectionId]; }; Projection.prototype.destroyInstance = function(projectionId, context) { var subscriberId, _i, _len, _ref; if (!this._handlerFunctions[projectionId]) { return eventric.log.error('Missing attribute projectionId'); } _ref = this._handlerFunctions[projectionId]; for (_i = 0, _len = _ref.length; _i < _len; _i++) { subscriberId = _ref[_i]; context.unsubscribeFromDomainEvent(subscriberId); } delete this._handlerFunctions[projectionId]; return delete this._projectionInstances[projectionId]; }; return Projection; })(); module.exports = new Projection;