Los value objects (VO) son objetos que se identifican por su contenido y nos ayudan a modelar conceptos de negocio.
- Respetan el principio de cohesión y encapsulamiento de responsabilidades.
- Soluciona Primitive Obsession
- Inmutabilidad
- No tienen identidad
Permite mantener la inmutabilidad en lenguajes funcionales y permite encapsular lógica de validación de un concepto de negocio que no puede residir en la entidad. Por ejemplo, si quieres dar de alta usuarios, dichos usuarios deberán tener un id, email y nombre como atributos. Cada atributo deberá contener su lógica de validación, con lo cual, utilizaremos el patrón value object para encapsular la lógica. Esto nos permite eliminar los servicios genéricos como el típico user service y respetar las características mencionadas anteriormente obteniendo un código mucho más testeable y alineado a los principios SOLID.
Ejemplo en typescript:
interface ValueObjectProps {
[index: string]: any;
}
export abstract class ValueObject<T extends ValueObjectProps> {
constructor(protected props: T) {
const baseProps: any = {
...props,
};
this.props = baseProps;
}
public equals(vo?: ValueObject<T>): boolean {
if (vo === null || vo === undefined) {
return false;
}
if (vo.props === undefined) {
return false;
}
return JSON.stringify(this.props) === JSON.stringify(vo.props);
}
}
export interface CredentialsData { email: Email; password: Password; } export class Credentials extends ValueObject<CredentialsData> implements CredentialsData { public readonly email: Email; public readonly password: Password; private constructor(data: CredentialsData) { super(data); this.email = data.email; this.password = data.password; } public static create( data: { email: string; password: string; } ): Either<ValidationError<Credentials>[], Credentials> { const emailResult = Email.create(data.email); const passwordResult = Password.create(data.password); const errors: ValidationError<Credentials>[] = [ { property: "email" as const, errors: emailResult.fold(errors => errors,() => []), value: data.email, }, { property: "password" as const, errors: passwordResult.fold(errors => errors,() => []), value: data.password, }, ] .map(error => ({ ...error, type: Credentials.name })) .filter(validation => validation.errors.length > 0); if (errors.length === 0) { return Either.right( new Credentials({ email: emailResult.get(), password: passwordResult.get() }) ); } else { return Either.left(errors); } } }
Deja una respuesta