rich-domain
Version:
This package provide utils file and interfaces to assistant build a complex application with domain driving design
146 lines • 7.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Entity = void 0;
const deep_freeze_util_1 = require("../utils/deep-freeze.util");
const auto_mapper_1 = require("./auto-mapper");
const entity_getters_and_setters_1 = require("./entity-getters-and-setters");
const id_1 = require("./id");
const result_1 = require("./result");
/**
* @description Represents a domain entity identified by a unique identifier (ID).
* Extends basic entity functionalities, ensuring the presence of `createdAt` and `updatedAt` timestamps and providing
* utility methods such as equality checks, object transformations, and instance creation.
*/
class Entity extends entity_getters_and_setters_1.default {
_id;
autoMapper;
constructor(props, config) {
super(Object.assign({}, { createdAt: new Date(), updatedAt: new Date() }, { ...props }), 'Entity', config);
if (typeof props !== 'object' || (props instanceof Date) || Array.isArray(props)) {
throw new Error(`Props must be an 'object' for entities, but received: '${typeof props}' as props on Class '${this.constructor.name}'`);
}
;
const isID = this.validator.isID(props?.['id']);
const isStringOrNumber = this.validator.isString(props?.['id']) || this.validator.isNumber(props?.['id']);
this._id = isStringOrNumber ? id_1.default.create(props?.['id']) : isID ? props?.['id'] : id_1.default.create();
this.autoMapper = new auto_mapper_1.default();
}
/**
* @description Determines if the current entity has the same properties (except `createdAt` and `updatedAt`) and the same ID as another entity.
* @param other The entity instance to compare against.
* @returns `true` if both entities have identical properties and IDs; otherwise, `false`.
*/
isEqual(other) {
const currentProps = { ...this?.props, id: null };
const providedProps = { ...other?.props, id: null };
delete currentProps?.['createdAt'];
delete currentProps?.['updatedAt'];
delete providedProps?.['createdAt'];
delete providedProps?.['updatedAt'];
const equalId = this.id.isEqual(other?.id);
const serializedA = JSON.stringify(currentProps);
const serializedB = JSON.stringify(providedProps);
const equalSerialized = serializedA === serializedB;
return equalId && equalSerialized;
}
/**
* @description Converts the current entity instance into a plain object representation.
* @param adapter An optional adapter or builder that transforms the entity into another object format.
* @returns If an adapter is provided, returns the adapted object. Otherwise, returns a deeply frozen object
* representing the entity properties along with entity metadata (`AutoMapperSerializer<Props> & EntityMapperPayload`).
*/
toObject(adapter) {
if (adapter && typeof adapter?.adaptOne === 'function') {
return adapter.adaptOne(this);
}
if (adapter && typeof adapter?.build === 'function') {
return adapter.build(this).value();
}
const serializedObject = this.autoMapper.entityToObj(this);
const frozenObject = (0, deep_freeze_util_1.deepFreeze)(serializedObject);
return frozenObject;
}
/**
* @description Retrieves the unique identifier (ID) of the entity.
* @returns An instance of `UID<string>` representing the entity's ID.
*/
get id() {
return this._id;
}
/**
* @description Generates a "hash code" like identifier for the entity, combining its class name and ID.
* @summary The format is `[Entity@ClassName]:UUID`. The ClassName is derived from the entity's prototype.
* This helps uniquely identify the entity instance in logs or debugging.
* @returns A `UID<string>` representing the hash code of the entity.
*/
hashCode() {
const name = Reflect.getPrototypeOf(this);
return id_1.default.create(`[Entity@${name?.constructor?.name}]:${this.id.value()}`);
}
/**
* @description Checks if the entity is newly created and not yet persisted or saved externally (e.g., to a database).
* @returns `true` if the entity is considered new (ID marked as new); otherwise, `false`.
*/
isNew() {
return this.id.isNew();
}
/**
* @description Creates a new entity instance based on the current entity.
* Allows overriding some properties. If no `id` is provided in the new props, a new one will be generated.
* @param props Optional partial properties to override when creating the new entity instance.
* @returns A new instance of the entity with updated properties.
*/
clone(props) {
const instance = Reflect.getPrototypeOf(this);
const _props = props ? { ...this.props, ...props } : { ...this.props };
const args = [_props, this.config];
return Reflect.construct(instance.constructor, args);
}
/**
* @description Validates if a given value is suitable for creating or representing an entity.
* @param value The value to validate.
* @returns `true` if the value is considered valid for the entity; otherwise, `false`.
*/
static isValid(value) {
return this.isValidProps(value);
}
;
/**
* @description Validates the provided properties to check if they can be used to create a valid entity instance.
* @param props The properties object to validate.
* @returns `true` if the props are valid; `false` otherwise.
*/
static isValidProps(props) {
return !this.validator.isUndefined(props) && !this.validator.isNull(props);
}
;
/**
* @description Initializes a new entity instance from the given properties.
* @summary This method should be implemented in subclasses. By default, it throws an error.
* @param props The properties to initialize the entity.
* @returns The newly created entity instance or throws an error if not implemented.
* @throws An error indicating the method is not implemented.
*/
static init(props) {
throw new Error('method not implemented: init', {
cause: props
});
}
;
/**
* @description Creates a new entity instance wrapped inside a `Result` object.
* @param props The properties to create the entity with. Must be valid properties.
* @param id (Optional) A UUID to assign to the entity. If not provided, a new one will be generated.
* @returns A `Result` instance containing the new entity if successfully created; otherwise, a failure `Result`.
* @summary If the properties are invalid, the result will be a failure with `null` state.
*/
static create(props) {
if (!this.isValidProps(props))
return result_1.default.fail('Invalid props to create an instance of ' + this.name);
return result_1.default.Ok(new this(props));
}
;
}
exports.Entity = Entity;
exports.default = Entity;
//# sourceMappingURL=entity.js.map