npm.io
0.30.0 • Published 2 months ago

@vytches/ddd-value-objects

Licence
MIT
Version
0.30.0
Deps
3
Size
36 kB
Vulns
0
Weekly
0

@vytches/ddd-value-objects

npm version TypeScript License: MIT

Base value object class, EntityId, and branded ID types

Provides the foundation for value objects in DDD — immutability, structural equality, and type-safe entity identifiers.

Installation

pnpm add @vytches/ddd-value-objects

What's included

Export Kind Description
BaseValueObject<T> class Abstract base for value objects; deep-freezes internal value; implements equals() via structural comparison
ValueObjectValidator<T> interface validate(value: T): boolean — implement to add invariant checking
EntityId<T> class Enhanced entity identifier extending the contracts EntityId; adds UUID/integer/text validation, create(), fromString(), fromNumber()
EntityIdFactory class Deprecated — will be removed in v1.0.0; use EntityId.create() directly
BrandedId<Tag> type Compile-time branded EntityId<string> — prevents mixing IDs across aggregate types
createBrandedId<Tag>(id) function Casts an EntityId to BrandedId<Tag>
newBrandedId<Tag>() function Creates a new UUID-based BrandedId<Tag>
brandedIdFromUUID<Tag>(uuid) function Creates a BrandedId<Tag> from an existing UUID string
brandedIdFromText<Tag>(text) function Creates a BrandedId<Tag> from a text string
IEntityId<T> interface Re-exported from @vytches/ddd-contracts
IEntityIdFactory interface Re-exported from @vytches/ddd-contracts
IdType type Re-exported from @vytches/ddd-contracts'uuid' | 'integer' | 'text'

Note on bundled domain examples

This package does not export Email, Money, Address, PhoneNumber, DateRange, or other concrete domain value objects. Those are application-level types — implement them in your own domain layer using BaseValueObject as the base class.

Quick start

Custom value object
import { BaseValueObject } from '@vytches/ddd-value-objects';

interface MoneyProps {
  amount: number;
  currency: string;
}

class Money extends BaseValueObject<MoneyProps> {
  constructor(amount: number, currency: string) {
    super({ amount, currency });
  }

  validate(value: MoneyProps): boolean {
    return value.amount >= 0 && value.currency.length === 3;
  }

  get amount(): number {
    return this.value.amount;
  }
  get currency(): string {
    return this.value.currency;
  }

  add(other: Money): Money {
    if (this.currency !== other.currency) throw new Error('Currency mismatch');
    return new Money(this.amount + other.amount, this.currency);
  }
}

const price = new Money(10, 'USD');
const tax = new Money(1.5, 'USD');
const total = price.add(tax);

console.log(total.equals(new Money(11.5, 'USD'))); // true
EntityId
import { EntityId } from '@vytches/ddd-value-objects';

// Generate a new UUID-based ID
const id = EntityId.create(); // EntityId<string> with UUID

// From an existing UUID string
const fromString = EntityId.fromString('550e8400-e29b-41d4-a716-446655440000');

// From an integer
const fromNumber = EntityId.fromNumber(42);

console.log(id.getValue()); // '550e8400-...'
console.log(id.equals(EntityId.fromString(id.getValue()))); // true
Branded IDs
import {
  BrandedId,
  newBrandedId,
  brandedIdFromUUID,
} from '@vytches/ddd-value-objects';

type OrderId = BrandedId<'Order'>;
type CustomerId = BrandedId<'Customer'>;

const orderId: OrderId = newBrandedId<'Order'>();
const customerId: CustomerId = newBrandedId<'Customer'>();

function shipOrder(id: OrderId): void {
  /* ... */
}

shipOrder(orderId); // OK
shipOrder(customerId); // TypeScript compile error!

Package boundaries

@vytches/ddd-value-objects depends on:

  • @vytches/ddd-contractsEntityId base class, IEntityId, IdType
  • @vytches/ddd-domain-primitives — error types
  • @vytches/ddd-utilsLibUtils, Result

License

MIT

Keywords