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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@typescript-eda/application

v1.0.0

Published

Application layer orchestration and dependency injection for TypeScript-EDA event-driven architecture

Readme

TypeScript-EDA Application Layer

The orchestration engine that coordinates domain logic with infrastructure capabilities

TypeScript License: GPL v3 npm version

Overview

The TypeScript-EDA Application layer provides a sophisticated orchestration framework for event-driven architectures. It serves as the conductor that coordinates domain logic with infrastructure adapters, enabling clean separation of concerns while maintaining powerful coordination capabilities.

Key Features

  • 🎭 Declarative Configuration: Use @Enable decorators to configure infrastructure adapters
  • 🔄 Event-Driven Orchestration: Queue-based event processing with cascading support
  • 🔌 Multiple Entry Points: HTTP, CLI, messaging, and WebSocket primary ports
  • 🧩 Dependency Injection: Automatic adapter wiring with type safety
  • 🛡️ Error Isolation: Resilient event processing with configurable recovery strategies
  • 🏗️ Lifecycle Management: Complete application startup, operation, and shutdown
  • 🧪 Test-Friendly: Comprehensive testing utilities and patterns

Quick Start

Installation

npm install @typescript-eda/application @typescript-eda/domain @typescript-eda/infrastructure
# or with pnpm
pnpm add @typescript-eda/application @typescript-eda/domain @typescript-eda/infrastructure

Basic Usage

import { Application, Enable } from '@typescript-eda/application';
import { PostgresUserRepository } from './infrastructure/database/postgres-user-repository';
import { EmailNotificationAdapter } from './infrastructure/notifications/email-notification-adapter';
import { ExpressWebServerAdapter } from './infrastructure/web/express-web-server-adapter';

@Enable(PostgresUserRepository)
@Enable(EmailNotificationAdapter)
@Enable(ExpressWebServerAdapter)
export class UserManagementApplication extends Application {
  public readonly metadata = new Map([
    ['name', 'User Management Application'],
    ['description', 'Complete user lifecycle management with event-driven architecture'],
    ['version', '1.0.0']
  ]);

  @listen(UserRegistrationRequested)
  public async handleUserRegistration(event: UserRegistrationRequested): Promise<Event[]> {
    const userRepository = Ports.resolve(UserRepository);
    const userId = new UserId(this.generateUserId());
    const user = new User(userId, event.email, event.name);
    
    await userRepository.save(user);
    
    return [
      new UserRegistered(userId, event.email, event.name),
      new EmailVerificationRequested(userId, event.email, this.generateVerificationToken())
    ];
  }
}

// Start the application
async function main() {
  const app = new UserManagementApplication();
  await app.start();
}

Core Concepts

Application Base Class

The Application base class provides the orchestration engine:

export abstract class Application {
  public abstract readonly metadata: Map<string, unknown>;
  
  public async handle(events: Event | Event[]): Promise<void>;
  public async start(): Promise<void>;
  public async shutdown(): Promise<void>;
}

@Enable Decorator

Declaratively configure infrastructure adapters:

@Enable(PostgresUserRepository)
@Enable(EmailNotificationAdapter)
@Enable(ExpressWebServerAdapter)
export class MyApplication extends Application {
  // Application configuration through metadata
}

Primary Ports

Entry points that drive your application:

@AdapterFor(WebServerPort)
export class ExpressWebServerAdapter implements PrimaryPort {
  public async accept(app: Application): Promise<void> {
    const server = express();
    
    server.post('/api/users', async (req, res) => {
      const event = new UserRegistrationRequested(req.body.email, req.body.name);
      await app.handle(event);
      res.json({ success: true });
    });
    
    server.listen(3000);
  }
}

Event Processing

The application layer processes events in a queue-based system with cascading support:

@listen(OrderPlaced)
public async coordinateOrderProcessing(event: OrderPlaced): Promise<Event[]> {
  return [
    new InventoryReservationRequested(event.orderId, event.items),
    new PaymentProcessingRequested(event.orderId, event.amount),
    new ShippingArrangementRequested(event.orderId, event.address)
  ];
}

Multi-Channel Applications

Support multiple entry points with the same business logic:

@Enable(PostgresUserRepository)
@Enable(EmailNotificationAdapter)
@Enable(ExpressWebServerAdapter)    // HTTP API
@Enable(UserCLIAdapter)             // Command line
@Enable(RabbitMQConsumerAdapter)    // Message queue
export class MultiChannelApplication extends Application {
  // Same logic, multiple interfaces
}

Testing

Application layer testing focuses on coordination logic:

describe('UserManagementApplication', () => {
  let application: UserManagementApplication;
  let mockUserRepository: jest.Mocked<UserRepository>;

  beforeEach(() => {
    mockUserRepository = createMockUserRepository();
    application = new UserManagementApplication();
    
    Ports.set(UserRepository, mockUserRepository);
  });

  it('should coordinate user registration flow', async () => {
    const event = new UserRegistrationRequested(
      new Email('[email protected]'),
      'Test User'
    );

    await application.handle(event);

    expect(mockUserRepository.save).toHaveBeenCalledWith(expect.any(User));
  });
});

Documentation

Advanced Features

Saga Pattern Support

export class OrderProcessingSaga {
  @listen(OrderPlaced)
  public async executeOrderSaga(event: OrderPlaced): Promise<Event[]> {
    return [
      new InventoryReservationRequested(event.orderId, event.items),
      new PaymentProcessingRequested(event.orderId, event.amount)
    ];
  }

  @listen(PaymentFailed)
  public async compensatePaymentFailure(event: PaymentFailed): Promise<Event[]> {
    return [
      new InventoryReservationCancelled(event.orderId),
      new CustomerNotificationRequested(event.customerId, 'payment_failed')
    ];
  }
}

Multi-Tenant Applications

@Enable(MultiTenantUserRepository)
@Enable(TenantAwareNotificationAdapter)
export class MultiTenantSaaSApplication extends Application {
  @listen(UserRegistrationRequested)
  public async handleTenantUserRegistration(event: UserRegistrationRequested): Promise<Event[]> {
    const tenantId = this.extractTenantId(event);
    
    TenantContext.set(tenantId);
    try {
      return await this.processRegistration(event);
    } finally {
      TenantContext.clear();
    }
  }
}

Performance Optimization

export class PerformantApplication extends Application {
  private eventProcessor: BatchedEventProcessor;
  
  constructor() {
    super();
    this.eventProcessor = new BatchedEventProcessor({
      batchSize: 100,
      batchTimeout: 1000
    });
  }

  public async handle(events: Event | Event[]): Promise<void> {
    return this.eventProcessor.process(events);
  }
}

Architecture

The application layer follows these principles:

  1. Coordination over Implementation: Applications orchestrate, they don't execute business logic
  2. Event-Driven by Default: All interactions flow through events
  3. Resilience First: Failure is expected and handled gracefully
  4. Platform Agnostic: Applications should run anywhere
  5. Developer Experience: Good architecture should feel natural to use

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.

Related Projects


Built with ❤️ by the TypeScript-EDA Team