npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@velony/domain

v3.3.3

Published

TypeScript library providing core building blocks for Domain-Driven Design (DDD) applications

Readme

@velony/domain

A TypeScript library providing core building blocks for Domain-Driven Design (DDD) applications.

Installation

npm install @velony/domain

Core Concepts

Entity

An object with a distinct identity that runs through time and different states.

import { Entity, Id } from '@velony/domain';

class UserId extends Id<string> {
  static create(value: string): UserId {
    return new UserId(value);
  }
}

class User extends Entity<UserId> {
  constructor(
    id: UserId,
    private name: string,
    private email: string
  ) {
    super(id);
  }

  getName(): string {
    return this.name;
  }

  getEmail(): string {
    return this.email;
  }
}

const userId = UserId.create('user-123');
const user = new User(userId, 'John Doe', '[email protected]');

Value Object

An immutable object defined by its attributes rather than a unique identifier.

import { ValueObject } from '@velony/domain';

class Email extends ValueObject<string> {
  private constructor(value: string) {
    super(value);
  }

  static create(value: string): Email {
    if (!value.includes('@')) {
      throw new Error('Invalid email format');
    }
    return new Email(value);
  }

  equals(other: Email): boolean {
    return this.value === other.value;
  }

  toString(): string {
    return this.value;
  }
}

const email = Email.create('[email protected]');

Primitive Value Object

A convenient base class for value objects wrapping primitive types.

import { PrimitiveValueObject } from '@velony/domain';

class Age extends PrimitiveValueObject<number> {
  private constructor(value: number) {
    super(value);
  }

  static create(value: number): Age {
    if (value < 0 || value > 150) {
      throw new Error('Invalid age');
    }
    return new Age(value);
  }
}

const age = Age.create(25);
console.log(age.toString()); // "25"

Aggregate Root

The entry point to an aggregate that maintains consistency boundaries and manages domain events.

import { AggregateRoot, Id, DomainEvent, DomainEventRegistry } from '@velony/domain';

class OrderId extends Id<string> {
  static create(value: string): OrderId {
    return new OrderId(value);
  }
}

// Register the event type in the registry
declare module '@velony/domain' {
  interface DomainEventRegistry {
    'order.placed': {
      aggregateId: OrderId;
      payload: { total: number };
    };
  }
}

class OrderPlacedEvent extends DomainEvent<'order.placed'> {}

class Order extends AggregateRoot<OrderId> {
  constructor(
    id: OrderId,
    private total: number
  ) {
    super(id);
  }

  place(): void {
    this.pushDomainEvent(
      new OrderPlacedEvent('order.placed', this.id, { total: this.total })
    );
  }
}

const order = new Order(OrderId.create('order-123'), 100);
order.place();
const events = order.pullDomainEvents();

Domain Event

Represents something significant that happened in the domain. Events use a type-safe registry pattern for compile-time validation.

import { DomainEvent, DomainEventRegistry, Id } from '@velony/domain';

class UserId extends Id<string> {
  static create(value: string): UserId {
    return new UserId(value);
  }
}

// Register event types in the DomainEventRegistry
declare module '@velony/domain' {
  interface DomainEventRegistry {
    'user.registered': {
      aggregateId: UserId;
      payload: {
        email: string;
        name: string;
      };
    };
  }
}

class UserRegisteredEvent extends DomainEvent<'user.registered'> {}

const userId = UserId.create('user-123');
const event = new UserRegisteredEvent('user.registered', userId, {
  email: '[email protected]',
  name: 'John Doe'
});
console.log(event.id); // UUIDv7
console.log(event.type); // "user.registered"
console.log(event.occurredAt); // Date
console.log(event.aggregateId); // UserId instance
console.log(event.payload); // { email: "[email protected]", name: "John Doe" }

StoragePath

A built-in value object for safe storage paths.

import { StoragePath } from '@velony/domain';

const path = StoragePath.create('uploads/images/photo.jpg');
console.log(path.extension); // "jpg"
console.log(path.toUrl('https://storage.example.com')); 
// "https://storage.example.com/uploads/images/photo.jpg"

// Validation prevents unsafe paths
StoragePath.create('/etc/passwd'); // Error: Storage path should not start with /
StoragePath.create('../secrets'); // Error: Storage path cannot contain ..
StoragePath.create('files//data'); // Error: Storage path contains invalid double slashes

API Reference

Entity<TIdentifier>

  • id: TIdentifier - The unique identifier
  • equals(other: this): boolean - Compare entities by identity

ValueObject<TValue>

  • value: TValue - The wrapped value
  • equals(other: this): boolean - Compare by value (abstract)
  • toString(): string - String representation (abstract)

PrimitiveValueObject<T>

  • Extends ValueObject<T> with default implementations for primitives
  • equals(other: this): boolean - Compares using strict equality
  • toString(): string - Converts value to string

Id<T>

  • Extends PrimitiveValueObject<T> for entity identifiers

AggregateRoot<TIdentifier>

  • Extends Entity<TIdentifier>
  • pullDomainEvents(): AnyDomainEvent[] - Retrieve and clear events
  • pushDomainEvent(event: AnyDomainEvent): void - Add event (public)

DomainEvent<TType>

  • id: string - Unique event ID (UUIDv7)
  • type: TType - Type identifier for the event (must be registered in DomainEventRegistry)
  • aggregateId: DomainEventRegistry[TType]['aggregateId'] - ID of the aggregate that produced the event
  • payload: DomainEventRegistry[TType]['payload'] - Event-specific data
  • occurredAt: Date - Timestamp of occurrence

DomainEventRegistry

Interface for registering event types with their aggregate ID and payload types. Extend this interface using module augmentation to register new event types:

declare module '@velony/domain' {
  interface DomainEventRegistry {
    'your.event.type': {
      aggregateId: YourAggregateId;
      payload: YourPayloadType;
    };
  }
}

License

MIT

Repository

https://github.com/velony-ai/domain

Issues

https://github.com/velony-ai/domain/issues