Domain-Driven Design - Value Objects in TypeScript
Before you look at the Value Objects example in TypeScript, you can see at my post on what Value Objects are in Domain-Driven Design.
Value Object base class in TypeScript
abstract class ValueObject<T> {
protected readonly value: T;
protected constructor(value: T) {
this.validate(value);
this.value = Object.freeze(value);
}
protected abstract validate(value: T): void;
public equals(other?: ValueObject<T>): boolean {
if (other === null || other === undefined) {
return false;
}
if (other.value === undefined) {
return false;
}
return deepEqual(this, other);
}
}
About this code snippet:
- It is
abstract
: This prevents this class from being instantiated and allows to declare abstract methods. - It calls an abstract method called Validate in the constructor: So we are validating the value of the Value Object before creating it.
- It use
Object.freeze()
so the value can no longer be changed. - It has a
equals
method that usesdeepEqual
, you can read about it in this post.
Value Object example in TypeScript
class FirstName extends ValueObject<string> {
public static readonly MaxLength = 64;
protected validate(value: string): void {
if (!value)
throw new InvalidFirstNameError();
if (value.length > FirstName.MaxLength)
throw new FirstNameIsTooLongError();
}
static create(value: string): FirstName {
return new FirstName(value);
}
public get getFirstName(): string {
return this.value;
}
}
About this code snippet:
- It overrides the Validate method with validation rules.
- No public
constructor
: Other classes cannot create instances of this class. - To create instances of this Value Object we do not call the constructor directly, but it uses the Static Factory Method pattern instead.