UNPKG

rich-domain

Version:

This package provide utils file and interfaces to assistant build a complex application with domain driving design

150 lines 6.85 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ValueObject = void 0; const deep_freeze_util_1 = require("../utils/deep-freeze.util"); const auto_mapper_1 = require("./auto-mapper"); const base_getters_and_setters_1 = require("./base-getters-and-setters"); const result_1 = require("./result"); /** * @description A `ValueObject` represents a domain object characterized by its properties rather than a unique identifier. * Commonly used in domain-driven design, value objects are immutable and should be structurally equal * (two value objects with the same properties are considered equal). * This class provides functionalities to: * - Validate properties * - Compare equality between value objects * - Convert the value object into a plain object representation * - Clone the value object */ class ValueObject extends base_getters_and_setters_1.default { autoMapper; /** * @description Initializes a new ValueObject instance. * @param props Properties that define the ValueObject. * @param config Optional configuration settings for getter/setter behavior. */ constructor(props, config) { super(props, config); this.autoMapper = new auto_mapper_1.default(); } /** * @description Determines if the current value object is equal to another value object of the same type. * Equality is defined by comparing properties, excluding `createdAt` and `updatedAt`. * Primitive values (strings, numbers, booleans), dates, arrays, and IDs are compared by value. * Complex object structures are compared by their JSON-serialized form (excluding `createdAt` and `updatedAt`). * * @param other The value object to compare against. * @returns `true` if all considered properties are equal; `false` otherwise. */ isEqual(other) { const props = this.props; const otherProps = other?.props; const stringifyAndOmit = (obj) => { if (!obj) return ''; const { createdAt, updatedAt, ...cleanedProps } = obj; return JSON.stringify(cleanedProps); }; if (this.validator.isString(props)) { return this.validator.string(props).isEqual(otherProps); } if (this.validator.isDate(props)) { return props.getTime() === otherProps?.getTime(); } if (this.validator.isArray(props) || this.validator.isFunction(props)) { return JSON.stringify(props) === JSON.stringify(otherProps); } if (this.validator.isBoolean(props)) { return props === otherProps; } if (this.validator.isID(props)) { return props.value() === otherProps?.value(); } if (this.validator.isNumber(props) || typeof props === 'bigint') { return this.validator.number(props).isEqualTo(otherProps); } if (this.validator.isUndefined(props) || this.validator.isNull(props)) { return props === otherProps; } if (this.validator.isSymbol(props)) { return props.description === otherProps?.description; } return stringifyAndOmit(props) === stringifyAndOmit(otherProps); } /** * @description Creates a new instance of the value object, optionally overriding properties. * Deep cloning is performed for object-based props, ensuring immutability of value objects. * * @param props Optional partial properties to override in the cloned instance. * @returns A new ValueObject instance with updated properties. */ clone(props) { const instance = Reflect.getPrototypeOf(this); if (typeof this.props === 'object' && !(this.props instanceof Date) && !Array.isArray(this.props)) { const _props = props ? { ...this.props, ...props } : { ...this.props }; const args = [_props, this.config]; return Reflect.construct(instance.constructor, args); } const args = [this.props, this.config]; return Reflect.construct(instance.constructor, args); } /** * @description Converts the value object into a plain object or a format defined by the given adapter. * If no adapter is provided, the object is serialized using an `AutoMapper` and frozen to ensure immutability. * * @param adapter Optional adapter to transform the value object into a custom format. * @returns A deeply frozen, plain object representation of the value object properties. */ 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.valueObjectToObj(this); const frozenObject = (0, deep_freeze_util_1.deepFreeze)(serializedObject); return frozenObject; } /** * @description Checks if a given value is considered valid for creating a ValueObject instance. * Subclasses can override this to apply additional validation logic. * @param value The value to validate. * @returns `true` if valid; `false` otherwise. */ static isValid(value) { return this.isValidProps(value); } /** * @description Validates the provided properties before creating a new value object instance. * @param props The properties to validate. * @returns `true` if the properties are valid; `false` otherwise. */ static isValidProps(props) { return !this.validator.isUndefined(props) && !this.validator.isNull(props); } /** * @description Intended to initialize a new value object instance with a given value. * This method should be implemented by subclasses as needed. * @param value The initial value or properties. * @throws An error indicating the method is not implemented. */ static init(value) { throw new Error('method not implemented: init', { cause: value }); } /** * @description Creates a new ValueObject instance wrapped inside a `Result`. * Returns a failure `Result` if the provided properties are invalid. * @param props The properties needed to create the value object. * @returns A `Result` containing the new ValueObject on success, or a failure `Result` on invalid properties. */ 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.ValueObject = ValueObject; exports.default = ValueObject; //# sourceMappingURL=value-object.js.map