rich-domain
Version:
This package provide utils file and interfaces to assistant build a complex application with domain driving design
219 lines • 9.29 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GettersAndSetters = void 0;
const util_1 = require("../utils/util");
const validator_1 = require("../utils/validator");
const create_many_domain_instance_1 = require("./create-many-domain-instance");
const id_1 = require("./id");
/**
* @description GettersAndSetters provides a foundational mechanism for retrieving and updating properties
* of domain objects (like Entities or Value Objects). It enables property validation and gives control
* over whether getters or setters are active. When integrated with Entities or Value Objects, this class
* ensures that changes to domain properties follow defined validation rules.
*/
class GettersAndSetters {
props;
validator = validator_1.default;
static validator = validator_1.default;
util = util_1.default;
static util = util_1.default;
parentName = 'ValueObject';
config = { disableGetters: false, disableSetters: false };
constructor(props, parentName, config) {
this.props = props;
GettersAndSetters.validator = validator_1.default;
GettersAndSetters.util = util_1.default;
this.validator = validator_1.default;
this.util = util_1.default;
this.config.disableGetters = !!config?.disableGetters;
this.config.disableSetters = !!config?.disableSetters;
this.parentName = parentName;
}
/**
* @description Creates multiple domain instances at once.
*
* @param data An array of options that includes class constructors and properties.
* @returns An object containing:
* - `result`: A combined result indicating overall success or failure.
* - `data`: An iterator of Result objects, each for an attempted instance creation.
*
* @example
* ```typescript
* const { result, data } = ValueObject.createMany([
* Class<AgeProps>(Age, props),
* Class<NameProps>(Name, props),
* Class<PriceProps>(Price, props)
* ]);
*
* if (result.isOk()) {
* const ageResult = data.next().value;
* const nameResult = data.next().value;
* const priceResult = data.next().value;
*
* console.log(ageResult.value().get('value')); // e.g. 21
* }
* ```
*/
static createMany(data) {
return (0, create_many_domain_instance_1.default)(data);
}
validation(_value, _key) {
return true;
}
/**
* @description Sets a property value after validating it. Returns a chained method
* (`to`) that accepts the new value and an optional validation function.
*
* @param key The property key to set.
* @returns An object with a `to` function to finalize the property assignment.
*
* @example
* ```typescript
* entity.set('age').to(30, (age) => age > 0);
* ```
*/
set(key) {
return {
/**
* @description Assigns the provided value to the specified property if it passes both
* the optional provided validation function and the class's `validation` method.
*
* @param value The value to assign to the property.
* @param validation An optional validation function that returns `true` if the value is valid.
* @returns `true` if the value was successfully assigned, otherwise throws an error.
*
* @example
* ```typescript
* entity.set('name').to('Alice', (value) => value.length > 0);
* ```
*/
to: (value, validation) => {
const instance = Reflect.getPrototypeOf(this);
if (this.config.disableSetters) {
throw new Error(`Attempted to set value "${value}" for key "${String(key)}" but setters are disabled on ${instance?.constructor.name}.`);
}
if (typeof validation === 'function' && !validation(value)) {
throw new Error(`Validation failed for value "${value}" on key "${String(key)}" in ${instance?.constructor.name}.`);
}
if (!this.validation(value, key)) {
throw new Error(`Validation failed for value "${value}" on key "${String(key)}" in ${instance?.constructor.name}.`);
}
// Special handling for ID on Entities
if (key === 'id' && this.parentName === 'Entity') {
if (this.validator.isString(value) || this.validator.isNumber(value)) {
this['_id'] = id_1.default.create(value);
this['props']['id'] = this['_id'].value();
if (this.parentName === 'Entity') {
this['props'] = { ...this['props'], updatedAt: new Date() };
}
return true;
}
if (this.validator.isID(value)) {
this['_id'] = value;
this['props']['id'] = this['_id'].value();
if (this.parentName === 'Entity') {
this['props'] = { ...this['props'], updatedAt: new Date() };
}
return true;
}
}
this.props[key] = value;
if (this.parentName === 'Entity') {
this['props'] = { ...this['props'], updatedAt: new Date() };
}
return true;
}
};
}
/**
* @description Changes the value of a specified property directly (without the chained approach).
* Validates the value using both the provided validation function (if any) and the class's `validation` method.
*
* @param key The property key to change.
* @param value The new value for the property.
* @param validation An optional validation function that returns `true` if the value is valid.
* @returns `true` if the value was successfully changed, otherwise throws an error.
*
* @example
* ```typescript
* entity.change('age', 25, (age) => age > 0);
* ```
*/
change(key, value, validation) {
const instance = Reflect.getPrototypeOf(this);
if (this.config.disableSetters) {
throw new Error(`Attempted to set value "${value}" for key "${String(key)}" but setters are disabled on ${instance?.constructor.name}.`);
}
if (typeof validation === 'function' && !validation(value)) {
throw new Error(`Validation failed for value "${value}" on key "${String(key)}" in ${instance?.constructor.name}.`);
}
if (!this.validation(value, key)) {
throw new Error(`Validation failed for value "${value}" on key "${String(key)}" in ${instance?.constructor.name}.`);
}
// Special handling for ID on Entities
if (key === 'id' && this.parentName === 'Entity') {
if (this.validator.isString(value) || this.validator.isNumber(value)) {
this['_id'] = id_1.default.create(value);
this['props']['id'] = this['_id'].value();
if (this.parentName === 'Entity') {
this['props'] = { ...this['props'], updatedAt: new Date() };
}
return true;
}
if (this.validator.isID(value)) {
this['_id'] = value;
this['props']['id'] = this['_id'].value();
if (this.parentName === 'Entity') {
this['props'] = { ...this['props'], updatedAt: new Date() };
}
return true;
}
}
this.props[key] = value;
if (this.parentName === 'Entity') {
this['props'] = { ...this['props'], updatedAt: new Date() };
}
return true;
}
/**
* @description Retrieves the value of a specified property.
*
* @param key The property key to retrieve.
* @returns The property's value as a read-only value.
*
* @throws Will throw an error if getters are disabled.
*
* @example
* ```typescript
* const age = entity.get('age');
* console.log(age); // e.g. 30
* ```
*/
get(key) {
if (this.config.disableGetters) {
const instance = Reflect.getPrototypeOf(this);
throw new Error(`Attempted to get key "${String(key)}" but getters are disabled on ${instance?.constructor.name}.`);
}
if (typeof this.props === 'object') {
return this.props?.[key] ?? null;
}
return this.props;
}
/**
* @description Returns the raw (immutable) properties object of the domain instance.
*
* @returns A frozen, read-only view of the properties.
*
* @example
* ```typescript
* const raw = entity.getRaw();
* console.log(raw); // returns a frozen object with all properties
* ```
*/
getRaw() {
return Object.freeze(this.props);
}
}
exports.GettersAndSetters = GettersAndSetters;
exports.default = GettersAndSetters;
//# sourceMappingURL=entity-getters-and-setters.js.map