@vytches/ddd-aggregates 0.29.3 → 0.31.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LLMGUIDE.md +84 -17
- package/README.md +5 -1
- package/dist/aggregate-interfaces.d.ts +16 -184
- package/dist/capabilities/audit-capability.d.ts +1 -1
- package/dist/capabilities/event-sourcing-capability.d.ts +1 -1
- package/dist/capabilities/snapshot-capability.d.ts +1 -1
- package/dist/capabilities/versioning-capability.d.ts +15 -2
- package/dist/core/aggregate-root.builder.d.ts +1 -1
- package/dist/core/aggregate-root.d.ts +93 -6
- package/dist/core/aggregate-utilities.d.ts +2 -2
- package/dist/core/entity.d.ts +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +255 -168
- package/package.json +8 -7
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("@vytches/ddd-contracts"),D=require("@vytches/ddd-logging"),o=require("@vytches/ddd-domain-primitives"),u=require("@vytches/ddd-utils");class d extends h.Capability{constructor(){super(...arguments),this.type="versioning",this.upcasters=new Map}static get capabilityType(){return"versioning"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this.upcasters.clear()}registerUpcaster(t,e,i){this.upcasters.has(t)||this.upcasters.set(t,new Map),this.upcasters.get(t).set(e,i)}handleVersionedEvent(t,e){const i=t.metadata?.version||1,a=t.metadata?.targetVersion||i;let n=t;if(i<a&&this.upcasters.has(t.eventName)){const c=this.upcasters.get(t.eventName),m=D.Logger.forContext("VersioningCapability");for(let l=i;l<a;l++){const S=c.get(l);if(!S){m.warn("Missing upcaster for event version — event will be processed without upcast",{eventName:t.eventName,fromVersion:l,toVersion:l+1});continue}const V=S.upcast(n.payload,n.metadata);n={...n,payload:V,metadata:{...n.metadata,version:l+1}}}}const g=e.get(n.eventName);g&&g(n.payload,n.metadata)}getRegisteredEventTypes(){return Array.from(this.upcasters.keys())}hasUpcaster(t,e){return this.upcasters.get(t)?.has(e)||!1}getUpcastersForType(t){return this.upcasters.get(t)}clearUpcastersForType(t){this.upcasters.delete(t)}getTotalUpcasterCount(){let t=0;for(const e of this.upcasters.values())t+=e.size;return t}}class p{constructor(t){this._version=0,this._initialVersion=0,this._domainEvents=[],this._eventHandlers=new Map,this._capabilities=new h.CapabilityRegistry,this._id=t.id,this._version=t.version||0,this._initialVersion=t.version||0,this._maxEvents=t.maxEvents}getId(){return this._id}getVersion(){return this._version}getInitialVersion(){return this._initialVersion}hasChanges(){return this._domainEvents.length>0}getDomainEvents(){return[...this._domainEvents]}commit(){this._domainEvents=[],this._initialVersion=this._version}registerEventHandler(t,e){return this._eventHandlers.set(t,e),this}apply(t,e,i){this._version++;let a,n;typeof t=="string"?(a=h.createDomainEvent(t,e,i),n=a.metadata):(a=t,n=i?{...a.metadata,...i}:a.metadata);const g=p.sanitizeMetadata({...n,aggregateId:this._id.toString(),aggregateType:this.constructor.name,aggregateVersion:this._version,timestamp:n?.timestamp??new Date}),c=Object.assign(Object.create(Object.getPrototypeOf(a)),a,{metadata:g});if(this._maxEvents!==void 0&&this._domainEvents.length>=this._maxEvents)throw new Error(`Aggregate ${this.constructor.name} (id=${this._id.toString()}) exceeded maxEvents limit of ${this._maxEvents}. This usually indicates a runaway loop or replay of a corrupted event stream. Increase maxEvents in the aggregate constructor params if the limit is too restrictive for your domain.`);this._domainEvents.push(c),this.handleEvent(c)}handleEvent(t){const e=this.getCapability(d);if(e)e.handleVersionedEvent(t,this._eventHandlers);else{const i=this._eventHandlers.get(t.eventName);i&&i(t.payload,t.metadata)}}loadFromHistory(t){this._version=this._initialVersion,this._domainEvents=[];for(const e of t)this.handleEvent(e),this._version++;this._initialVersion=this._version}addCapability(t){return"attach"in t&&typeof t.attach=="function"&&t.attach(this),this._capabilities.register(t),this}getCapability(t){return this._capabilities.get(t)}hasCapability(t){return this._capabilities.has(t)}removeCapability(t){const e=this._capabilities.get(t);return e&&"detach"in e&&typeof e.detach=="function"&&e.detach(),this._capabilities.remove(t),this}getAllCapabilities(){return this._capabilities.getAll()}getCapabilityTypes(){return this._capabilities.getTypes()}static sanitizeMetadata(t){if(!Object.prototype.hasOwnProperty.call(t,"__proto__")&&!Object.prototype.hasOwnProperty.call(t,"constructor")&&!Object.prototype.hasOwnProperty.call(t,"prototype"))return t;const e={};for(const i of Object.keys(t))i==="__proto__"||i==="constructor"||i==="prototype"||(e[i]=t[i]);return e}_internal_setState(t){this._version=t.version,this._initialVersion=t.initialVersion,this._domainEvents=[...t.domainEvents]}}class f extends h.Capability{constructor(){super(...arguments),this.type="audit",this.auditLog=[]}static get capabilityType(){return"audit"}attach(t){this.aggregate=t;const e=this.aggregate;typeof e.apply=="function"&&(this.originalApply=e.apply,this.originalApply&&(this.aggregate.apply=(...i)=>{const a=this.originalApply.call(this.aggregate,...i);return this.recordAuditEvent(),a}))}detach(){this.aggregate&&this.originalApply&&(this.aggregate.apply=this.originalApply),this.aggregate=void 0,this.auditLog=[],this.originalApply=void 0}getAuditLog(){return[...this.auditLog]}clearAuditLog(){this.auditLog=[]}getAuditStatistics(){const t={};let e=0;this.auditLog.forEach((a,n)=>{if(t[a.eventName]=(t[a.eventName]||0)+1,n>0){const g=this.auditLog[n-1];if(g){const c=new Date(g.timestamp).getTime(),m=new Date(a.timestamp).getTime();e+=m-c}}});const i=this.auditLog.length>1?e/(this.auditLog.length-1):0;return{totalEvents:this.auditLog.length,eventsByType:t,averageTimeBetweenEvents:i}}recordAuditEvent(){const t=this.aggregate.getDomainEvents(),e=t[t.length-1];e&&this.recordEvent(e)}recordEvent(t){const e={eventId:t.metadata?.eventId||`audit-${Date.now()}-${Math.random()}`,eventName:t.eventName,aggregateId:this.aggregate.getId().toString(),aggregateType:this.aggregate.constructor.name,aggregateVersion:this.aggregate.getVersion(),timestamp:new Date,payload:t.payload,metadata:{...t.metadata,auditCapability:!0},actor:t.metadata?.userId};this.auditLog.push(e)}getEventsByName(t){return this.auditLog.filter(e=>e.eventName===t)}getEventsByTimeRange(t,e){return this.auditLog.filter(i=>{const a=new Date(i.timestamp).getTime();return a>=t.getTime()&&a<=e.getTime()})}getFirstEvent(){return this.auditLog[0]||null}getLastEvent(){return this.auditLog[this.auditLog.length-1]||null}}class r extends o.IDomainError{static invalidArguments(t,e){const i={code:o.DomainErrorCode.InvalidParameter,data:e};return new r(t,i)}static versionConflict(t,e,i,a){const n=`Version conflict: Aggregate ${t} with ID ${e} has version ${i}, but expected ${a}`,g={code:o.DomainErrorCode.ValidationFailed,data:{aggregateType:t,aggregateId:e,currentVersion:i,expectedVersion:a}};return new r(n,g)}static featureNotEnabled(t){const e=`Feature '${t}' is not enabled on aggregate`,i={code:o.DomainErrorCode.ValidationFailed,data:{feature:t}};return new r(e,i)}static methodNotImplemented(t,e){const i=`Method '${t}' must be implemented by ${e} to use this feature`,a={code:o.DomainErrorCode.MissingValue,data:{methodName:t,aggregateType:e}};return new r(i,a)}static invalidSnapshot(t,e){const i=`Invalid snapshot for aggregate ${t}${e?`: ${e}`:""}`,a={code:o.DomainErrorCode.InvalidFormat,data:{aggregateType:t,reason:e}};return new r(i,a)}static idMismatch(t,e){const i=`ID mismatch: Snapshot is for ID ${t}, but aggregate has ID ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{snapshotId:t,aggregateId:e}};return new r(i,a)}static typeMismatch(t,e){const i=`Aggregate type mismatch: Snapshot is for type ${t}, but trying to load into ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{snapshotType:t,aggregateType:e}};return new r(i,a)}static duplicateUpcaster(t,e){const i=`Upcaster for event ${t} version ${e} already exists`,a={code:o.DomainErrorCode.DuplicateEntry,data:{eventType:t,sourceVersion:e}};return new r(i,a)}static missingUpcaster(t,e,i){const a=`Missing upcaster for event ${t} from version ${e} to ${i}`,n={code:o.DomainErrorCode.MissingValue,data:{eventType:t,fromVersion:e,toVersion:i}};return new r(a,n)}static configurationError(t,e){const i=`Configuration error in ${t}: ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{aggregateType:t,configurationError:e}};return new r(i,a)}static cannotInterceptApplyMethod(t,e){const i=`Cannot intercept apply method - method not found on aggregate ${t}`,a={code:o.DomainErrorCode.InternalError,data:{aggergateId:t,...e}};return new r(i,a)}static eventStoreNotConfigured(t){const e="Event store not configured",i={code:o.DomainErrorCode.MissingValue,data:t};return new r(e,i)}static aggregateDoesNotSupportReplay(t,e){const i=`Aggregate ${t} does not support event replay`,a={code:o.DomainErrorCode.MethodNotSupported,data:{aggregateType:t,...e}};return new r(i,a)}}class y extends h.Capability{constructor(){super(...arguments),this.type="eventSourcing",this.eventStore=null}static get capabilityType(){return"eventSourcing"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this.eventStore=null}setEventStore(t){this.eventStore=t}getEventStore(){return this.eventStore}async loadFromEventStore(t){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const e=await this.eventStore.getEvents(t);if(e.length===0)return;const i=this.aggregate;i.loadFromHistory&&i.loadFromHistory(e)}async saveToEventStore(){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const t=this.aggregate.getDomainEvents();t.length!==0&&await this.eventStore.saveEvents(this.aggregate.getId().toString(),[...t],this.aggregate.getInitialVersion())}hasEventStore(){return this.eventStore!==null}getStreamName(){return`${this.aggregate.constructor.name}-${this.aggregate.getId().toString()}`}async loadFromVersion(t,e){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const i=await this.eventStore.getEventsAfterVersion(t,e),a=this.aggregate;a.loadFromHistory&&i.length>0&&a.loadFromHistory(i)}}class E extends h.Capability{constructor(){super(...arguments),this.type="snapshot",this._snapshot=null}static get capabilityType(){return"snapshot"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this._snapshot=null}createSnapshot(t,e){const i=this.aggregate.getDomainEvents(),n=(i.length>0?i[i.length-1]:null)?.metadata?.eventId,c={aggregateId:this.aggregate.getId().toString(),version:this.aggregate.getVersion(),aggregateType:this.aggregate.constructor.name,state:t(),timestamp:new Date,...n!==void 0&&{lastEventId:n}};return e&&(c.metadata=e()),c}restoreFromSnapshot(t,e,i){if(!t||!t.state)throw r.invalidSnapshot(this.aggregate.constructor.name,"Invalid snapshot");const a=this.aggregate.getId().toString();if(t.aggregateId!==a)throw r.idMismatch(t.aggregateId,a);if(t.aggregateType!==this.aggregate.constructor.name)throw r.typeMismatch(t.aggregateType,this.aggregate.constructor.name);e(t.state),t.metadata&&i&&i(t.metadata);const n=this.aggregate;n._internal_setState&&n._internal_setState({version:t.version,initialVersion:t.version,domainEvents:[]})}saveTemporaryState(t){this._snapshot=this.createSnapshot(()=>t)}getLastSnapshotTimestamp(){return this._snapshot?.timestamp||null}saveSnapshot(t,e){this._snapshot=this.createSnapshot(t,e)}getPreviousState(){const t=this._snapshot;return this._snapshot=null,t}}class v{constructor(t){this.capabilities=[],this.params=t}static create(t){const e={id:t.id instanceof h.EntityId?t.id:new h.EntityId(t.id,"text"),version:t.version||0};return new v(e)}withSnapshots(){return this.capabilities.push({capability:new E}),this}withVersioning(){return this.capabilities.push({capability:new d}),this}withAudit(){return this.capabilities.push({capability:new f}),this}withEventSourcing(t){const e=new y;return this.capabilities.push({capability:e,configure:i=>{const a=i;t?a.setEventStore(t):this.eventStore&&a.setEventStore(this.eventStore)}}),this}withCapability(t,e){return this.capabilities.push({capability:t,configure:e?i=>e(i):void 0}),this}setEventStore(t){return this.eventStore=t,this}build(t){const e=t||p,i=new e(this.params);for(const{capability:a,configure:n}of this.capabilities)i.addCapability(a),n&&n(a);return i}buildWithAllCapabilities(t){return this.withSnapshots().withVersioning().withAudit().withEventSourcing().build(t)}}function I(s){return v.create(s)}class b{constructor(t){this._id=t}getId(){return this._id}equals(t){return t==null?!1:t===this?!0:t instanceof b?this._id.equals(t._id):!1}}function w(s){return s.hasCapability(E)}function C(s){return s.hasCapability(d)}function _(s){return s.hasCapability(f)}function A(s){return s.hasCapability(y)}function $(s){if(!w(s))throw r.featureNotEnabled("snapshot");return s}function N(s){return w(s)?u.Result.ok(s):u.Result.fail(r.featureNotEnabled("snapshot"))}function L(s){if(!C(s))throw r.featureNotEnabled("versioning");return s}function M(s){return C(s)?u.Result.ok(s):u.Result.fail(r.featureNotEnabled("versioning"))}function T(s){if(!_(s))throw r.featureNotEnabled("audit");return s}function F(s){return _(s)?u.Result.ok(s):u.Result.fail(r.featureNotEnabled("audit"))}function x(s){if(!A(s))throw r.featureNotEnabled("eventSourcing");return s}function R(s){return A(s)?u.Result.ok(s):u.Result.fail(r.featureNotEnabled("eventSourcing"))}function U(s){return s.getCapabilityTypes()}function H(s,t){return t.every(e=>s.hasCapability(e))}exports.AggregateBuilder=v;exports.AggregateError=r;exports.AggregateRoot=p;exports.AuditCapability=f;exports.Entity=b;exports.EventSourcingCapability=y;exports.SnapshotCapability=E;exports.VersioningCapability=d;exports.aggregateBuilder=I;exports.asAuditAggregate=T;exports.asEventSourcingAggregate=x;exports.asSnapshotAggregate=$;exports.asVersioningAggregate=L;exports.getAggregateCapabilities=U;exports.hasAllCapabilities=H;exports.tryAsAuditAggregate=F;exports.tryAsEventSourcingAggregate=R;exports.tryAsSnapshotAggregate=N;exports.tryAsVersioningAggregate=M;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("@vytches/ddd-contracts"),S=require("@vytches/ddd-contracts/internal"),h=require("@vytches/ddd-utils"),o=require("@vytches/ddd-domain-primitives");class p extends u.Capability{constructor(){super(...arguments),this.type="versioning",this.upcasters=new Map}static get capabilityType(){return"versioning"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this.upcasters.clear()}registerUpcaster(t,e,i){this.upcasters.has(t)||this.upcasters.set(t,new Map),this.upcasters.get(t).set(e,i)}handleVersionedEvent(t,e){const i=t.metadata?.version||1,a=t.metadata?.targetVersion||i;let n=t;if(i<a&&this.upcasters.has(t.eventName)){const l=this.upcasters.get(t.eventName);for(let c=i;c<a;c++){const E=l.get(c);if(!E){S.internalLogger.warn("VersioningCapability: Missing upcaster for event version — event will be processed without upcast",{eventName:t.eventName,fromVersion:c,toVersion:c+1});continue}const I=E.upcast(n.payload,n.metadata);n={...n,payload:I,metadata:{...n.metadata,version:c+1}}}}const g=e.get(n.eventName);g&&g(n.payload,n.metadata)}getRegisteredEventTypes(){return Array.from(this.upcasters.keys())}hasUpcaster(t,e){return this.upcasters.get(t)?.has(e)||!1}getUpcastersForType(t){return this.upcasters.get(t)}clearUpcastersForType(t){this.upcasters.delete(t)}getTotalUpcasterCount(){let t=0;for(const e of this.upcasters.values())t+=e.size;return t}}const w=Symbol("vytches-ddd-aggregate-internal-state");class d{constructor(t){this._version=0,this._initialVersion=0,this._domainEvents=[],this._eventHandlers=new Map,this._capabilities=new u.CapabilityRegistry,this._id=t.id,this._version=t.version||0,this._initialVersion=t.version||0,this._maxEvents=t.maxEvents,this._onMissingHandler=t.onMissingHandler??"warn"}getId(){return this._id}equals(t){return t==null?!1:t===this?!0:t instanceof d?this._id.equals(t._id):!1}getVersion(){return this._version}getInitialVersion(){return this._initialVersion}hasChanges(){return this._domainEvents.length>0}getDomainEvents(){const t=this._domainEvents.map(e=>h.LibUtils.deepFreeze(e));return Object.freeze(t)}commit(){this._domainEvents=[],this._initialVersion=this._version}registerEventHandler(t,e){return this._eventHandlers.set(t,e),this}apply(t,e,i){if(this._maxEvents!==void 0&&this._domainEvents.length>=this._maxEvents)throw new Error(`Aggregate ${this.constructor.name} (id=${this._id.toString()}) exceeded maxEvents limit of ${this._maxEvents}. This usually indicates a runaway loop or replay of a corrupted event stream. Increase maxEvents in the aggregate constructor params if the limit is too restrictive for your domain.`);const a=this._version+1;let n,g;typeof t=="string"?(n=u.createDomainEvent(t,e,i),g=n.metadata):(n=t,g=i?{...n.metadata,...i}:n.metadata);const l=d.sanitizeMetadata({...g,aggregateId:this._id.toString(),aggregateType:this.constructor.name,aggregateVersion:a,timestamp:g?.timestamp??new Date}),c=Object.assign(Object.create(Object.getPrototypeOf(n)),n,{metadata:l});this._version=a,this._domainEvents.push(c),this.handleEvent(c)}handleEvent(t){const e=this.getCapability(p);if(e)e.handleVersionedEvent(t,this._eventHandlers);else{const i=this._eventHandlers.get(t.eventName);i?i(t.payload,t.metadata):this.reportMissingHandler(t.eventName)}}reportMissingHandler(t){const e=`Aggregate ${this.constructor.name} (id=${this._id.toString()}) has no registered handler for event "${t}". State will not be updated for this event. Register a handler via registerEventHandler('${t}', ...) before applying/replaying this event, or pass onMissingHandler: "warn" explicitly to silence this in the constructor params.`;if(this._onMissingHandler==="throw")throw new Error(e);S.internalLogger.warn(e,{aggregateType:this.constructor.name,aggregateId:this._id.toString(),eventName:t})}loadFromHistory(t){this._version=this._initialVersion,this._domainEvents=[];for(const e of t)this.handleEvent(e),this._version++;this._initialVersion=this._version}addCapability(t){return"attach"in t&&typeof t.attach=="function"&&t.attach(this),this._capabilities.register(t),this}getCapability(t){return this._capabilities.get(t)}hasCapability(t){return this._capabilities.has(t)}removeCapability(t){const e=this._capabilities.get(t);return e&&"detach"in e&&typeof e.detach=="function"&&e.detach(),this._capabilities.remove(t),this}getAllCapabilities(){return this._capabilities.getAll()}getCapabilityTypes(){return this._capabilities.getTypes()}static sanitizeMetadata(t){if(!Object.prototype.hasOwnProperty.call(t,"__proto__")&&!Object.prototype.hasOwnProperty.call(t,"constructor")&&!Object.prototype.hasOwnProperty.call(t,"prototype"))return t;const e={};for(const i of Object.keys(t))i==="__proto__"||i==="constructor"||i==="prototype"||(e[i]=t[i]);return e}_internal_setState(t,e){if(t!==w)throw new TypeError("_internal_setState: invalid or missing internal state token. This method is restricted to capabilities within @vytches/ddd-aggregates.");this._version=e.version,this._initialVersion=e.initialVersion,this._domainEvents=[...e.domainEvents]}}class m extends u.Capability{constructor(){super(...arguments),this.type="audit",this.auditLog=[]}static get capabilityType(){return"audit"}attach(t){this.aggregate=t;const e=this.aggregate;typeof e.apply=="function"&&(this.originalApply=e.apply,this.originalApply&&(this.aggregate.apply=(...i)=>{const a=this.originalApply.call(this.aggregate,...i);return this.recordAuditEvent(),a}))}detach(){this.aggregate&&this.originalApply&&(this.aggregate.apply=this.originalApply),this.aggregate=void 0,this.auditLog=[],this.originalApply=void 0}getAuditLog(){return[...this.auditLog]}clearAuditLog(){this.auditLog=[]}getAuditStatistics(){const t={};let e=0;this.auditLog.forEach((a,n)=>{if(t[a.eventName]=(t[a.eventName]||0)+1,n>0){const g=this.auditLog[n-1];if(g){const l=new Date(g.timestamp).getTime(),c=new Date(a.timestamp).getTime();e+=c-l}}});const i=this.auditLog.length>1?e/(this.auditLog.length-1):0;return{totalEvents:this.auditLog.length,eventsByType:t,averageTimeBetweenEvents:i}}recordAuditEvent(){const t=this.aggregate.getDomainEvents(),e=t[t.length-1];e&&this.recordEvent(e)}recordEvent(t){const e={eventId:t.metadata?.eventId||`audit-${h.LibUtils.getUUID()}`,eventName:t.eventName,aggregateId:this.aggregate.getId().toString(),aggregateType:this.aggregate.constructor.name,aggregateVersion:this.aggregate.getVersion(),timestamp:new Date,payload:t.payload,metadata:{...t.metadata,auditCapability:!0},actor:t.metadata?.userId};this.auditLog.push(e)}getEventsByName(t){return this.auditLog.filter(e=>e.eventName===t)}getEventsByTimeRange(t,e){return this.auditLog.filter(i=>{const a=new Date(i.timestamp).getTime();return a>=t.getTime()&&a<=e.getTime()})}getFirstEvent(){return this.auditLog[0]||null}getLastEvent(){return this.auditLog[this.auditLog.length-1]||null}}class r extends o.IDomainError{static invalidArguments(t,e){const i={code:o.DomainErrorCode.InvalidParameter,data:e};return new r(t,i)}static versionConflict(t,e,i,a){const n=`Version conflict: Aggregate ${t} with ID ${e} has version ${i}, but expected ${a}`,g={code:o.DomainErrorCode.ValidationFailed,data:{aggregateType:t,aggregateId:e,currentVersion:i,expectedVersion:a}};return new r(n,g)}static featureNotEnabled(t){const e=`Feature '${t}' is not enabled on aggregate`,i={code:o.DomainErrorCode.ValidationFailed,data:{feature:t}};return new r(e,i)}static methodNotImplemented(t,e){const i=`Method '${t}' must be implemented by ${e} to use this feature`,a={code:o.DomainErrorCode.MissingValue,data:{methodName:t,aggregateType:e}};return new r(i,a)}static invalidSnapshot(t,e){const i=`Invalid snapshot for aggregate ${t}${e?`: ${e}`:""}`,a={code:o.DomainErrorCode.InvalidFormat,data:{aggregateType:t,reason:e}};return new r(i,a)}static idMismatch(t,e){const i=`ID mismatch: Snapshot is for ID ${t}, but aggregate has ID ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{snapshotId:t,aggregateId:e}};return new r(i,a)}static typeMismatch(t,e){const i=`Aggregate type mismatch: Snapshot is for type ${t}, but trying to load into ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{snapshotType:t,aggregateType:e}};return new r(i,a)}static duplicateUpcaster(t,e){const i=`Upcaster for event ${t} version ${e} already exists`,a={code:o.DomainErrorCode.DuplicateEntry,data:{eventType:t,sourceVersion:e}};return new r(i,a)}static missingUpcaster(t,e,i){const a=`Missing upcaster for event ${t} from version ${e} to ${i}`,n={code:o.DomainErrorCode.MissingValue,data:{eventType:t,fromVersion:e,toVersion:i}};return new r(a,n)}static configurationError(t,e){const i=`Configuration error in ${t}: ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{aggregateType:t,configurationError:e}};return new r(i,a)}static cannotInterceptApplyMethod(t,e){const i=`Cannot intercept apply method - method not found on aggregate ${t}`,a={code:o.DomainErrorCode.InternalError,data:{aggergateId:t,...e}};return new r(i,a)}static eventStoreNotConfigured(t){const e="Event store not configured",i={code:o.DomainErrorCode.MissingValue,data:t};return new r(e,i)}static aggregateDoesNotSupportReplay(t,e){const i=`Aggregate ${t} does not support event replay`,a={code:o.DomainErrorCode.MethodNotSupported,data:{aggregateType:t,...e}};return new r(i,a)}}class f extends u.Capability{constructor(){super(...arguments),this.type="eventSourcing",this.eventStore=null}static get capabilityType(){return"eventSourcing"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this.eventStore=null}setEventStore(t){this.eventStore=t}getEventStore(){return this.eventStore}async loadFromEventStore(t){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const e=await this.eventStore.getEvents(t);if(e.length===0)return;const i=this.aggregate;i.loadFromHistory&&i.loadFromHistory(e)}async saveToEventStore(){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const t=this.aggregate.getDomainEvents();t.length!==0&&await this.eventStore.saveEvents(this.aggregate.getId().toString(),[...t],this.aggregate.getInitialVersion())}hasEventStore(){return this.eventStore!==null}getStreamName(){return`${this.aggregate.constructor.name}-${this.aggregate.getId().toString()}`}async loadFromVersion(t,e){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const i=await this.eventStore.getEventsAfterVersion(t,e),a=this.aggregate;a.loadFromHistory&&i.length>0&&a.loadFromHistory(i)}}class y extends u.Capability{constructor(){super(...arguments),this.type="snapshot",this._snapshot=null}static get capabilityType(){return"snapshot"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this._snapshot=null}createSnapshot(t,e){const i=this.aggregate.getDomainEvents(),n=(i.length>0?i[i.length-1]:null)?.metadata?.eventId,l={aggregateId:this.aggregate.getId().toString(),version:this.aggregate.getVersion(),aggregateType:this.aggregate.constructor.name,state:t(),timestamp:new Date,...n!==void 0&&{lastEventId:n}};return e&&(l.metadata=e()),l}restoreFromSnapshot(t,e,i){if(!t||!t.state)throw r.invalidSnapshot(this.aggregate.constructor.name,"Invalid snapshot");const a=this.aggregate.getId().toString();if(t.aggregateId!==a)throw r.idMismatch(t.aggregateId,a);if(t.aggregateType!==this.aggregate.constructor.name)throw r.typeMismatch(t.aggregateType,this.aggregate.constructor.name);e(t.state),t.metadata&&i&&i(t.metadata),this.aggregate._internal_setState(w,{version:t.version,initialVersion:t.version,domainEvents:[]})}saveTemporaryState(t){this._snapshot=this.createSnapshot(()=>t)}getLastSnapshotTimestamp(){return this._snapshot?.timestamp||null}saveSnapshot(t,e){this._snapshot=this.createSnapshot(t,e)}getPreviousState(){const t=this._snapshot;return this._snapshot=null,t}}class v{constructor(t){this.capabilities=[],this.params=t}static create(t){const e={id:t.id instanceof u.EntityId?t.id:new u.EntityId(t.id,"text"),version:t.version||0};return new v(e)}withSnapshots(){return this.capabilities.push({capability:new y}),this}withVersioning(){return this.capabilities.push({capability:new p}),this}withAudit(){return this.capabilities.push({capability:new m}),this}withEventSourcing(t){const e=new f;return this.capabilities.push({capability:e,configure:i=>{const a=i;t?a.setEventStore(t):this.eventStore&&a.setEventStore(this.eventStore)}}),this}withCapability(t,e){return this.capabilities.push({capability:t,configure:e?i=>e(i):void 0}),this}setEventStore(t){return this.eventStore=t,this}build(t){const e=t||d,i=new e(this.params);for(const{capability:a,configure:n}of this.capabilities)i.addCapability(a),n&&n(a);return i}buildWithAllCapabilities(t){return this.withSnapshots().withVersioning().withAudit().withEventSourcing().build(t)}}function D(s){return v.create(s)}class b{constructor(t){this._id=t}getId(){return this._id}equals(t){return t==null?!1:t===this?!0:t instanceof b?this._id.equals(t._id):!1}}function _(s){return s.hasCapability(y)}function C(s){return s.hasCapability(p)}function A(s){return s.hasCapability(m)}function V(s){return s.hasCapability(f)}function $(s){if(!_(s))throw r.featureNotEnabled("snapshot");return s}function M(s){return _(s)?h.Result.ok(s):h.Result.fail(r.featureNotEnabled("snapshot"))}function N(s){if(!C(s))throw r.featureNotEnabled("versioning");return s}function T(s){return C(s)?h.Result.ok(s):h.Result.fail(r.featureNotEnabled("versioning"))}function L(s){if(!A(s))throw r.featureNotEnabled("audit");return s}function F(s){return A(s)?h.Result.ok(s):h.Result.fail(r.featureNotEnabled("audit"))}function H(s){if(!V(s))throw r.featureNotEnabled("eventSourcing");return s}function x(s){return V(s)?h.Result.ok(s):h.Result.fail(r.featureNotEnabled("eventSourcing"))}function U(s){return s.getCapabilityTypes()}function R(s,t){return t.every(e=>s.hasCapability(e))}exports.AggregateBuilder=v;exports.AggregateError=r;exports.AggregateRoot=d;exports.AuditCapability=m;exports.Entity=b;exports.EventSourcingCapability=f;exports.SnapshotCapability=y;exports.VersioningCapability=p;exports.aggregateBuilder=D;exports.asAuditAggregate=L;exports.asEventSourcingAggregate=H;exports.asSnapshotAggregate=$;exports.asVersioningAggregate=N;exports.getAggregateCapabilities=U;exports.hasAllCapabilities=R;exports.tryAsAuditAggregate=F;exports.tryAsEventSourcingAggregate=x;exports.tryAsSnapshotAggregate=M;exports.tryAsVersioningAggregate=T;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { AuditCapability } from './capabilities/audit-capability';
|
|
|
5
5
|
export { EventSourcingCapability } from './capabilities/event-sourcing-capability';
|
|
6
6
|
export { SnapshotCapability } from './capabilities/snapshot-capability';
|
|
7
7
|
export { VersioningCapability } from './capabilities/versioning-capability';
|
|
8
|
-
export type { IAggregateRoot, IAggregateCapability, IAggregateConstructorParams, IAggregateEventHandler,
|
|
8
|
+
export type { IAggregateRoot, IAggregateCapability, IAggregateConstructorParams, IAggregateEventHandler, } from './aggregate-interfaces';
|
|
9
9
|
export { AggregateError } from './aggregate-errors';
|
|
10
10
|
export type { AggregateWithSnapshotCapability, AggregateWithVersioningCapability, AggregateWithAuditCapability, AggregateWithEventSourcingCapability, } from './core/aggregate-utilities';
|
|
11
11
|
export { asSnapshotAggregate, tryAsSnapshotAggregate, asVersioningAggregate, tryAsVersioningAggregate, asAuditAggregate, tryAsAuditAggregate, asEventSourcingAggregate, tryAsEventSourcingAggregate, getAggregateCapabilities, hasAllCapabilities, } from './core/aggregate-utilities';
|