demeine
Version:
DDDD - Distributed Domain Driven Design
217 lines (216 loc) • 11.7 kB
JavaScript
"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;
}();