UNPKG

demeine

Version:

DDDD - Distributed Domain Driven Design

217 lines (216 loc) 11.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "Repository", { enumerable: true, get: function() { return Repository; } }); var _bluebird = /*#__PURE__*/ _interopRequireDefault(require("bluebird")); var _uuid = require("uuid"); var _defaultFactory = require("../aggregate/DefaultFactory"); var _errorUtils = require("../utils/errorUtils"); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var LOG = require("slf").Logger.getLogger("demeine:repository"); var Repository = /*#__PURE__*/ function() { "use strict"; function Repository(partition, aggregateType, factory, concurrencyStrategy) { var options = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : {}; _classCallCheck(this, Repository); this._partition = partition; this._factory = factory || (0, _defaultFactory.DefaultFactory)(aggregateType); this._aggregateType = aggregateType; this._concurrencyStrategy = concurrencyStrategy; var _resetSnapshotOnFail; this._resetSnapshotOnFail = (_resetSnapshotOnFail = options.resetSnapshotOnFail) !== null && _resetSnapshotOnFail !== void 0 ? _resetSnapshotOnFail : true; } var _proto = Repository.prototype; _proto.findById = function findById(id, callback) { LOG.info("%s findById(%s)", this._aggregateType, id); if (this._partition.queryStreamWithSnapshot !== undefined) { return this.findByQueryStreamWithSnapshot(id, false, callback); } else if (this._partition.loadSnapshot !== undefined) { return this.findBySnapshot(id, false, callback); } else { var aggregate = this._factory(id); return this._partition.openStream(id).then(function(stream) { var events = stream.getCommittedEvents(); var version = stream.getVersion(); aggregate._rehydrate(events, version); return aggregate; }).nodeify(callback); } }; _proto.findBySnapshot = function findBySnapshot(id, isRetry, callback) { var _this = this; var ref, ref1; LOG.info("%s findBySnapshot(%s)", this._aggregateType, id); var loadSnapshot = (ref = this._partition.loadSnapshot) === null || ref === void 0 ? void 0 : ref.bind(this._partition); var queryStream = (ref1 = this._partition.queryStream) === null || ref1 === void 0 ? void 0 : ref1.bind(this._partition); if (!loadSnapshot) { throw new Error('Trying to find aggregate by snapshot, but partition is missing "loadSnapshot" method.'); } if (!queryStream) { throw new Error('Trying to find aggregate by snapshot, but partition is missing "queryStream" method.'); } var aggregate = this._factory(id); return loadSnapshot(id).then(function(aggregateSnapshot) { var ref; var version = (ref = aggregateSnapshot === null || aggregateSnapshot === void 0 ? void 0 : aggregateSnapshot.version) !== null && ref !== void 0 ? ref : 0; var snapshot = aggregateSnapshot === null || aggregateSnapshot === void 0 ? void 0 : aggregateSnapshot.snapshot; return queryStream(id, version).then(function(commits) { var events = commits.flatMap(function(commit) { return commit.events; }); var newVersion = version + events.length; try { aggregate._rehydrate(events, newVersion, snapshot); } catch (e) { if (_this._partition.removeSnapshot && _this._resetSnapshotOnFail && !isRetry) { // check if this is a retry to prevent infinite loop // delete snapshot and retry... return _this._partition.removeSnapshot(id).then(function() { return _this.findBySnapshot(id, true, callback); }); } else { throw e; } } return aggregate; }); }).nodeify(callback); }; _proto.findByQueryStreamWithSnapshot = function findByQueryStreamWithSnapshot(id, isRetry, callback) { var _this = this; var ref; LOG.info("%s findByQueryStreamWithSnapshot(%s)", this._aggregateType, id); var queryStreamWithSnapshot = (ref = this._partition.queryStreamWithSnapshot) === null || ref === void 0 ? void 0 : ref.bind(this._partition); if (!queryStreamWithSnapshot) { throw new Error('Trying to find aggregate by query stream with snapshot, but partition is missing "queryStreamWithSnapshot" method.'); } var aggregate = this._factory(id); return queryStreamWithSnapshot(id).then(function(response) { var commits = response.commits; var aggregateSnapshot = response.snapshot; var snapshot = aggregateSnapshot === null || aggregateSnapshot === void 0 ? void 0 : aggregateSnapshot.snapshot; var events = commits.flatMap(function(commit) { return commit.events; }); var ref; var newVersion = ((ref = aggregateSnapshot === null || aggregateSnapshot === void 0 ? void 0 : aggregateSnapshot.version) !== null && ref !== void 0 ? ref : 0) + events.length; try { aggregate._rehydrate(events, newVersion, snapshot); } catch (e) { if (_this._partition.removeSnapshot && _this._resetSnapshotOnFail && !isRetry) { // check if this is a retry to prevent infinite loop // delete snapshot and retry... return _this._partition.removeSnapshot(id).then(function() { return _this.findByQueryStreamWithSnapshot(id, true, callback); }); } else { throw e; } } return aggregate; }).nodeify(callback); }; _proto.findEventsById = function findEventsById(id, callback) { LOG.info("%s findEventsById(%s)", this._aggregateType, id); return this._partition.openStream(id).then(function(stream) { return stream.getCommittedEvents(); }).nodeify(callback); }; _proto.checkConcurrencyStrategy = function checkConcurrencyStrategy(aggregate, stream, uncommittedEvents) { var _this = this; var isNewStream = stream._version === -1; var shouldThrow = false; return new _bluebird.default(function(resolve) { if (!isNewStream && _this._concurrencyStrategy) { var numberOfEvents = uncommittedEvents.length; var nextStreamVersion = stream._version + numberOfEvents; if (nextStreamVersion > aggregate._version) { // if _concurrencyStrategy has two arguments, we need to load up all events, since client requested it. var writeOnly = _this._concurrencyStrategy ? _this._concurrencyStrategy.length < 2 : true; if (writeOnly) { shouldThrow = _this._concurrencyStrategy(uncommittedEvents); resolve(shouldThrow); } else { _this._partition.openStream(aggregate.id, false).then(function(newStream) { shouldThrow = _this._concurrencyStrategy ? _this._concurrencyStrategy(uncommittedEvents, newStream.getCommittedEvents()) : shouldThrow; resolve(shouldThrow); }); } } else { resolve(shouldThrow); } } else { resolve(shouldThrow); } }); }; _proto._getDeleteEvent = function _getDeleteEvent(events) { for(var i = 0; i < events.length; i++){ if (events[i].type === "$stream.deleted.event") { return events[i]; } } return null; }; _proto._delete = function _delete(aggregate, deleteEvent) { return this._partition.delete(aggregate.id, deleteEvent); }; _proto.save = function save(aggregate, commitId, callback) { var _this = this; var savingWithId = commitId; return this._partition.openStream(aggregate.id, true).then(function(stream) { return aggregate.getUncommittedEventsAsync().then(function(uncommittedEvents) { var deleteEvent = _this._getDeleteEvent(uncommittedEvents); if (deleteEvent) { return _this._delete(aggregate, deleteEvent); } else { var startAggregateVersion = aggregate.getVersion() - uncommittedEvents.length; var startStreamVersion = stream._version; return _this.checkConcurrencyStrategy(aggregate, stream, uncommittedEvents).then(function(shouldThrow) { if (shouldThrow === true) { throw new Error("Concurrency error. Version mismatch on stream"); } aggregate.clearUncommittedEvents(); savingWithId = savingWithId || (0, _uuid.v4)(); uncommittedEvents.forEach(function(event) { LOG.debug("%s append event - %s", _this._aggregateType, event.id); stream.append(event); }); return stream.commit(savingWithId).then(function() { LOG.info("Aggregate: %s committed %d events with id: %s", _this._aggregateType, uncommittedEvents.length, savingWithId); if (_this._partition.storeSnapshot !== undefined && aggregate._getSnapshot) { LOG.debug("Persisting snapshot for stream %s version %s", aggregate.id, aggregate.getVersion()); if (startStreamVersion > startAggregateVersion) { LOG.warn("IGNORING SNAPSHOT STORE. VERSION MISMATCH MIGHT LEAD TO SNAPSHOT FAILURE. for stream %s version %s - start stream version: %s - start aggregate version: %s", aggregate.id, aggregate.getVersion(), startStreamVersion, startAggregateVersion); } else { LOG.debug("Persisting snapshot for stream %s version %s - start stream version: %s - start aggregate version: %s", aggregate.id, aggregate.getVersion(), startStreamVersion, startAggregateVersion); _this._partition.storeSnapshot(aggregate.id, aggregate._getSnapshot(), aggregate.getVersion()); } } return aggregate; }).error(function(err) { LOG.debug("Unable to save commit id: %s for type: %s, with %d events. Error: %s", savingWithId, _this._aggregateType, uncommittedEvents.length, (0, _errorUtils.getMessageFromError)(err)); throw err; }); }); } }); }).nodeify(callback); }; return Repository; }();