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

@aseguragonzalez/ts-seedwork

v1.1.0

Published

Shared domain, application, and infrastructure building blocks (DDD seedwork).

Downloads

334

Readme

@aseguragonzalez/ts-seedwork

CI npm version License: MIT Node.js >=22

DDD and CQRS building blocks for TypeScript/Node applications.

Provides base classes and interfaces for the domain, application, and infrastructure layers of a domain-driven design. Zero runtime dependencies.

Goal

  • Unify patterns: All domain and application code extends or implements SeedWork abstractions, keeping the codebase consistent and predictable.
  • Keep the domain pure: Domain types depend only on SeedWork domain types; no framework or infrastructure in the domain layer.
  • Clear boundaries: Application use cases are expressed as command handlers (writes) and query handlers (reads), with primitives-only DTOs at the port boundary.

Installation

From npm

npm install @aseguragonzalez/ts-seedwork

From GitHub Packages

Add a .npmrc to your project:

@aseguragonzalez:registry=https://npm.pkg.github.com

Then authenticate with a personal access token with read:packages and install:

npm install @aseguragonzalez/ts-seedwork

Pre-release versions

Pre-release builds are published from pull request branches for integration testing. Install by tag:

npm install @aseguragonzalez/ts-seedwork@pr-42

Or by exact version (shown in the workflow's Job Summary after publishing):

npm install @aseguragonzalez/[email protected]

Pre-release dist-tags are removed automatically when the pull request closes. See CONTRIBUTING.md for how to publish one.

How to use

1. Define a domain aggregate

import { AggregateRoot, BaseDomainEvent, TypedDomainEvent, ValueObject } from '@aseguragonzalez/ts-seedwork';

class AccountId extends ValueObject {
  constructor(public readonly value: string) {
    super();
  }
}

class AccountOpened extends BaseDomainEvent<{ accountId: string; balance: number }> {
  static create(accountId: string, balance: number) {
    return new AccountOpened({ accountId, balance });
  }
  private constructor(payload: { accountId: string; balance: number }) {
    super(payload);
  }
}

class BankAccount extends AggregateRoot<AccountId> {
  private constructor(
    id: AccountId,
    public readonly balance: number,
    events: ReadonlyArray<TypedDomainEvent<Record<string, unknown>>> = []
  ) {
    super(id, events);
  }

  static open(id: AccountId, initialBalance: number): BankAccount {
    const event = AccountOpened.create(id.value, initialBalance);
    return new BankAccount(id, initialBalance, [event]);
  }

  static reconstitute(id: AccountId, balance: number): BankAccount {
    return new BankAccount(id, balance); // no events — already published
  }
}

2. Implement a command handler

import { Command, CommandHandler, ValidationErrorDetail, ValidationErrors } from '@aseguragonzalez/ts-seedwork';

class OpenAccountCommand implements Command {
  constructor(
    public readonly accountId: string,
    public readonly balance: number
  ) {}
  validate(): void {
    const errors: ValidationErrorDetail[] = [];
    if (this.balance < 0) errors.push({ code: 'INVALID_BALANCE', message: 'Balance cannot be negative' });
    if (errors.length > 0) throw new ValidationErrors(errors);
  }
}

class OpenAccountHandler implements CommandHandler<OpenAccountCommand> {
  constructor(private readonly repository: BankAccountRepository) {}

  async execute(command: OpenAccountCommand): Promise<void> {
    const id = new AccountId(command.accountId);
    const account = BankAccount.open(id, command.balance);
    await this.repository.save(account); // publishes AccountOpened automatically
  }
}

3. Assemble the bus

import { CommandBusBuilder, DomainEventPublishingRepository } from '@aseguragonzalez/ts-seedwork';

const repository = new DomainEventPublishingRepository(new BankAccountRepositoryImpl(), myEventPublisher);

const bus = new CommandBusBuilder()
  .register(OpenAccountCommand, new OpenAccountHandler(repository))
  .withValidation() // outermost — validates before opening a transaction
  .withTransaction(unitOfWork)
  .build();

const result = await bus.dispatch(new OpenAccountCommand('acc-1', 1000));
if (result.isFail()) {
  console.error(result.errors);
}

What's included

| Layer | Components | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Domain | Entity, AggregateRoot, ValueObject, BaseDomainEvent, Repository, UnitOfWork, DomainError, Logger | | Application | Command/CommandBus/CommandHandler, Query/QueryBus/QueryHandler, Result, Maybe, DomainEventPublisher, DomainEventHandler | | Infrastructure | RegistryCommandBus, RegistryQueryBus, TransactionalCommandBus, ValidationCommandBus, ValidationQueryBus, DomainEventPublishingRepository, CommandBusBuilder, QueryBusBuilder |

Built with

  • TypeScript 6
  • Node.js 22
  • Jest 30 + @swc/jest for tests
  • ESLint 10 for linting
  • Prettier 3 for formatting
  • Husky + commitlint for pre-commit hooks
  • semantic-release for automated versioning and changelog

Development

If you plan to contribute, read CONTRIBUTING.md for the full setup guide, architecture principles, and pull request process.

| Task | Command | | ----------------- | ------------------------------------------------------------------------------------- | | Install deps | npm ci | | Full quality gate | npm run lint && npm run format:check && npm run type:check && npm run test:coverage | | Tests | npm test | | Tests (coverage) | npm run test:coverage | | Build | npm run build |

Documentation

Requirements

  • Node.js 22+
  • TypeScript 6+

Contributing

See CONTRIBUTING.md.

References

This package draws on the following literature and on the experience of building solid, scalable, and maintainable systems in different stacks (PHP, C#, Python, TypeScript).

  • Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software 1
  • Vaughn Vernon, Implementing Domain-Driven Design 2
  • Robert C. Martin, Clean Architecture: A Craftsman's Guide to Software Structure and Design 3
  • .NET Microservices: Architecture for Containerized .NET Applications 4
  • Architecture Patterns with Python, Harry Percival & Bob Gregory 5

License

MIT