eventric
Version:
behavior-first application development
280 lines (265 loc) • 11.5 kB
JavaScript
var Projection,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Projection = (function() {
function Projection(_eventric) {
this._eventric = _eventric;
this._applyDomainEventToProjection = __bind(this._applyDomainEventToProjection, this);
this.log = this._eventric.log;
this._handlerFunctions = {};
this._projectionInstances = {};
this._domainEventsApplied = {};
}
Projection.prototype.initializeInstance = function(projectionName, Projection, params, _context) {
this._context = _context;
return new Promise((function(_this) {
return function(resolve, reject) {
var aggregateId, diFn, diName, projection, projectionId, _ref;
if (typeof Projection === 'function') {
projection = new Projection;
} else {
projection = Projection;
}
if (_this._context._di) {
_ref = _this._context._di;
for (diName in _ref) {
diFn = _ref[diName];
projection[diName] = diFn;
}
}
projectionId = _this._eventric.generateUid();
aggregateId = null;
projection.$subscribeHandlersWithAggregateId = function(_aggregateId) {
return aggregateId = _aggregateId;
};
_this.log.debug("[" + _this._context.name + "] Clearing ProjectionStores " + projection.stores + " of " + projectionName);
return _this._clearProjectionStores(projection.stores, projectionName).then(function() {
_this.log.debug("[" + _this._context.name + "] Finished clearing ProjectionStores of " + projectionName);
return _this._injectStoresIntoProjection(projectionName, projection);
}).then(function() {
return _this._callInitializeOnProjection(projectionName, projection, params);
}).then(function() {
_this.log.debug("[" + _this._context.name + "] Replaying DomainEvents against Projection " + projectionName);
return _this._parseEventNamesFromProjection(projection);
}).then(function(eventNames) {
return _this._applyDomainEventsFromStoreToProjection(projectionId, projection, eventNames, aggregateId);
}).then(function(eventNames) {
_this.log.debug("[" + _this._context.name + "] Finished Replaying DomainEvents against Projection " + projectionName);
return _this._subscribeProjectionToDomainEvents(projectionId, projectionName, projection, eventNames, aggregateId);
}).then(function() {
var event;
_this._projectionInstances[projectionId] = projection;
event = {
id: projectionId,
projection: projection
};
_this._context.publish("projection:" + projectionName + ":initialized", event);
_this._context.publish("projection:" + projectionId + ":initialized", event);
return resolve(projectionId);
})["catch"](function(err) {
return reject(err);
});
};
})(this));
};
Projection.prototype._callInitializeOnProjection = function(projectionName, projection, params) {
return new Promise((function(_this) {
return function(resolve, reject) {
if (!projection.initialize) {
_this.log.debug("[" + _this._context.name + "] No initialize function on Projection " + projectionName + " given, skipping");
return resolve(projection);
}
_this.log.debug("[" + _this._context.name + "] Calling initialize on Projection " + projectionName);
return projection.initialize(params, function() {
_this.log.debug("[" + _this._context.name + "] Finished initialize call on Projection " + projectionName);
return resolve(projection);
});
};
})(this));
};
Projection.prototype._injectStoresIntoProjection = function(projectionName, projection) {
return new Promise((function(_this) {
return function(resolve, reject) {
if (!projection.stores) {
return resolve();
}
if (projection["$store"] == null) {
projection["$store"] = {};
}
return _this._eventric.eachSeries(projection.stores, function(projectionStoreName, next) {
_this.log.debug("[" + _this._context.name + "] Injecting ProjectionStore " + projectionStoreName + " into Projection " + projectionName);
return _this._context.getProjectionStore(projectionStoreName, projectionName).then(function(projectionStore) {
if (projectionStore) {
projection["$store"][projectionStoreName] = projectionStore;
_this.log.debug("[" + _this._context.name + "] Finished Injecting ProjectionStore " + projectionStoreName + " into Projection " + projectionName);
return next();
}
})["catch"](function(err) {
return next(err);
});
}, function(err) {
if (err) {
return reject(err);
}
return resolve();
});
};
})(this));
};
Projection.prototype._clearProjectionStores = function(projectionStores, projectionName) {
return new Promise((function(_this) {
return function(resolve, reject) {
if (!projectionStores) {
return resolve();
}
return _this._eventric.eachSeries(projectionStores, function(projectionStoreName, next) {
_this.log.debug("[" + _this._context.name + "] Clearing ProjectionStore " + projectionStoreName + " for " + projectionName);
return _this._context.clearProjectionStore(projectionStoreName, projectionName).then(function() {
_this.log.debug("[" + _this._context.name + "] Finished clearing ProjectionStore " + projectionStoreName + " for " + projectionName);
return next();
})["catch"](function(err) {
return next(err);
});
}, function(err) {
return resolve();
});
};
})(this));
};
Projection.prototype._parseEventNamesFromProjection = function(projection) {
return new Promise((function(_this) {
return function(resolve, reject) {
var eventName, eventNames, key, value;
eventNames = [];
for (key in projection) {
value = projection[key];
if ((key.indexOf('handle')) === 0 && (typeof value === 'function')) {
eventName = key.replace(/^handle/, '');
eventNames.push(eventName);
}
}
return resolve(eventNames);
};
})(this));
};
Projection.prototype._applyDomainEventsFromStoreToProjection = function(projectionId, projection, eventNames, aggregateId) {
return new Promise((function(_this) {
return function(resolve, reject) {
var findEvents;
_this._domainEventsApplied[projectionId] = {};
if (aggregateId) {
findEvents = _this._context.findDomainEventsByNameAndAggregateId(eventNames, aggregateId);
} else {
findEvents = _this._context.findDomainEventsByName(eventNames);
}
return findEvents.then(function(domainEvents) {
if (!domainEvents || domainEvents.length === 0) {
return resolve(eventNames);
}
return _this._eventric.eachSeries(domainEvents, function(domainEvent, next) {
return _this._applyDomainEventToProjection(domainEvent, projection).then(function() {
_this._domainEventsApplied[projectionId][domainEvent.id] = true;
return next();
});
}, function(err) {
if (err) {
return reject(err);
}
return resolve(eventNames);
});
})["catch"](function(err) {
return reject(err);
});
};
})(this));
};
Projection.prototype._subscribeProjectionToDomainEvents = function(projectionId, projectionName, projection, eventNames, aggregateId) {
return new Promise((function(_this) {
return function(resolve, reject) {
var domainEventHandler;
domainEventHandler = function(domainEvent, done) {
if (done == null) {
done = function() {};
}
if (_this._domainEventsApplied[projectionId][domainEvent.id]) {
return done();
}
return _this._applyDomainEventToProjection(domainEvent, projection).then(function() {
var event;
_this._domainEventsApplied[projectionId][domainEvent.id] = true;
event = {
id: projectionId,
projection: projection,
domainEvent: domainEvent
};
_this._context.publish("projection:" + projectionName + ":changed", event);
_this._context.publish("projection:" + projectionId + ":changed", event);
return done();
})["catch"](function(err) {
return done(err);
});
};
return _this._eventric.eachSeries(eventNames, function(eventName, done) {
var subscriberPromise;
if (aggregateId) {
subscriberPromise = _this._context.subscribeToDomainEventWithAggregateId(eventName, aggregateId, domainEventHandler);
} else {
subscriberPromise = _this._context.subscribeToDomainEvent(eventName, domainEventHandler);
}
return subscriberPromise.then(function(subscriberId) {
var _base;
if ((_base = _this._handlerFunctions)[projectionId] == null) {
_base[projectionId] = [];
}
_this._handlerFunctions[projectionId].push(subscriberId);
return done();
})["catch"](function(err) {
return done(err);
});
}, function(err) {
if (err) {
return reject(err);
}
return resolve();
});
};
})(this));
};
Projection.prototype._applyDomainEventToProjection = function(domainEvent, projection) {
return new Promise((function(_this) {
return function(resolve, reject) {
var handleDomainEvent;
if (!projection["handle" + domainEvent.name]) {
_this.log.debug("Tried to apply DomainEvent '" + domainEvent.name + "' to Projection without a matching handle method");
resolve();
return;
}
handleDomainEvent = projection["handle" + domainEvent.name](domainEvent);
return Promise.all([handleDomainEvent]).then(function(_arg) {
var result;
result = _arg[0];
return resolve(result);
});
};
})(this));
};
Projection.prototype.getInstance = function(projectionId) {
return this._projectionInstances[projectionId];
};
Projection.prototype.destroyInstance = function(projectionId) {
var subscriberId, unsubscribePromises, _i, _len, _ref;
if (!this._handlerFunctions[projectionId]) {
return this._eventric.log.error('Missing attribute projectionId');
}
unsubscribePromises = [];
_ref = this._handlerFunctions[projectionId];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
subscriberId = _ref[_i];
unsubscribePromises.push(this._context.unsubscribeFromDomainEvent(subscriberId));
}
delete this._handlerFunctions[projectionId];
delete this._projectionInstances[projectionId];
return Promise.all(unsubscribePromises);
};
return Projection;
})();
module.exports = Projection;