rich-domain
Version:
This package provide utils file and interfaces to assistant build a complex application with domain driving design
159 lines • 6.98 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Aggregate = void 0;
const events_1 = require("./events");
const entity_1 = require("./entity");
const id_1 = require("./id");
const result_1 = require("./result");
const context_1 = require("./context");
/**
* @description Represents an aggregate identified by a unique ID, extending entity functionalities.
* It manages domain events internally, facilitates event dispatching, and provides metrics
* on event-related operations.
*/
class Aggregate extends entity_1.default {
_domainEvents;
_dispatchEventsAmount;
constructor(props, config, events) {
super(props, config);
this._dispatchEventsAmount = 0;
this._domainEvents = new events_1.default(this);
if (events)
this._domainEvents = events;
}
/**
* @description Generates a unique hash code for the aggregate, combining the class name and its ID.
* Useful for identification in logs or diagnostic information.
* @example
* `[Aggregate@ClassName]:UUID`
* @summary Class name is determined at runtime from the prototype.
* @returns A `UID<string>` representing the aggregate's hash code.
*/
hashCode() {
const name = Reflect.getPrototypeOf(this);
return id_1.default.create(`[Aggregate@${name?.constructor.name}]:${this.id.value()}`);
}
/**
* @description Returns the global event manager for the current context, enabling
* the registration (subscribe) and dispatching of events at the application level.
* @returns The application's `EventManager` instance.
*/
context() {
return context_1.default.events();
}
/**
* @description Provides event-related metrics for the aggregate.
* @property current - The number of currently stored (undispatched) events.
* @property total - The total number of events, including those already dispatched.
* @property dispatch - The number of events already dispatched by the aggregate.
* @returns An `EventMetrics` object containing event metrics.
*/
get eventsMetrics() {
return {
current: this._domainEvents.metrics.totalEvents(),
total: this._domainEvents.metrics.totalEvents() + this._dispatchEventsAmount,
dispatch: this._dispatchEventsAmount
};
}
/**
* @description Creates a new aggregate instance based on the current one.
* Allows overriding some properties. If no `id` is provided in the new properties,
* a new one will be generated.
* @param props Optional partial properties to override for the new instance.
* The `copyEvents` property can be used to copy current events to the new instance.
* @returns A new aggregate instance with updated properties.
*/
clone(props) {
const _props = props ? { ...this.props, ...props } : { ...this.props };
const _events = (props && !!props.copyEvents) ? this._domainEvents : null;
const instance = Reflect.getPrototypeOf(this);
const args = [_props, this.config, _events];
const aggregate = Reflect.construct(instance.constructor, args);
return aggregate;
}
/**
* @description Dispatches a specific event from the aggregate, incrementing the count of dispatched events.
* @param eventName The name of the event to dispatch.
* @param args Additional arguments passed to the event handler.
* @returns `void` or `Promise<void>` if the event is asynchronous.
*/
dispatchEvent(eventName, ...args) {
this._domainEvents.dispatchEvent(eventName, args);
this._dispatchEventsAmount++;
}
/**
* @description Dispatches all currently stored events in the aggregate, marking them as dispatched
* and updating the total count of dispatched events.
* @returns A Promise that resolves to `void` after all events are dispatched.
*/
async dispatchAll() {
const current = this._domainEvents.metrics.totalEvents();
await this._domainEvents.dispatchEvents();
this._dispatchEventsAmount += current;
}
;
/**
* @description Removes all currently stored events in the aggregate.
* @param config Optional configuration. If `resetMetrics` is `true`,
* the count of previously dispatched events is reset to zero.
* @returns `void`.
*/
clearEvents(config = { resetMetrics: false }) {
if (config.resetMetrics)
this._dispatchEventsAmount = 0;
this._domainEvents.clearEvents();
}
;
addEvent(eventNameOrEvent, handler, options) {
if (typeof eventNameOrEvent === 'string' && handler) {
this._domainEvents.addEvent(eventNameOrEvent, handler ?? null, options);
return;
}
const _options = eventNameOrEvent?.params?.options;
const eventName = eventNameOrEvent?.params?.eventName;
const eventHandler = eventNameOrEvent?.dispatch;
this._domainEvents.addEvent(eventName, eventHandler ?? null, _options);
}
/**
* @description Removes all events matching the provided event name.
* @param eventName The name of the event to remove.
* @returns The number of events removed.
*/
deleteEvent(eventName) {
const totalBefore = this._domainEvents.metrics.totalEvents();
this._domainEvents.removeEvent(eventName);
return totalBefore - this._domainEvents.metrics.totalEvents();
}
/**
* @description Creates a new aggregate instance wrapped inside a `Result` object.
* If the provided properties are invalid, returns a failure `Result`.
*
* @param props Properties used to create the aggregate.
* @param id (optional) A UUID to assign to the aggregate. If not provided, a new one will be generated.
* @returns A `Result` instance containing the new aggregate if successful. On failure, returns a `Result` with null state.
*
* @example
* ```typescript
* const result = MyAggregate.create({ name: "example" });
* if (result.isFailure) {
* console.error(result.error); // More explicit error message guiding the user to fix invalid properties
* } else {
* const aggregate = result.getValue();
* // Use the aggregate
* }
* ```
*
* @summary On failure, the error message clearly instructs the user to ensure all required properties
* are provided and have valid values.
*/
static create(props) {
if (!this.isValidProps(props))
return result_1.default.fail(`Failed to create an instance of ${this.name} due to invalid properties. ` +
`Please ensure that all required fields are provided and that the values are valid.`);
return result_1.default.Ok(new this(props));
}
;
}
exports.Aggregate = Aggregate;
exports.default = Aggregate;
//# sourceMappingURL=aggregate.js.map