Actor Model toolkit for TypeScript: Fault-tolerant, message-driven concurrency.
DomoActors provides a robust implementation of the Actor Model for TypeScript, enabling you to build concurrent, distributed, and fault-tolerant applications. Built on proven patterns from Reactive Architecture and Domain-Driven Design, DomoActors brings the power of message-driven actors to the TypeScript ecosystem.
This work is based on the Java version of XOOM/Actors, which I began experimenting on in 2012 and released into open source in 2016. The Java-based product burgeoned into a complete reactive platform.
- Core Actor Model: Full-featured actor implementation with lifecycle management
- Fault Tolerance: Hierarchical supervision with configurable strategies (Resume, Restart, Stop, Escalate)
- Flexible Messaging: Multiple mailbox implementations (ArrayMailbox, BoundedMailbox with overflow policies)
- Type Safety: Full TypeScript support with Protocol-based type-safe messaging
- Value Registry: Register and share runtime objects (databases, configuration, services) across all actors
- Testing Support: Comprehensive TestKit with utilities for asynchronous testing
- Observable State: Built-in state management with reactive updates
- Scheduling: Task scheduling with cancellation support
- Dead Letters: Dead letter handling for undeliverable messages
- Child Actors: Hierarchical actor creation and management
- Clean API: Intuitive, well-documented API designed for developer productivity
npm install domo-actorsimport { Actor, Protocol, stage } from 'domo-actors'
// Define your actor's protocol
interface Counter {
increment(): void
decrement(): void
getValue(): void
}
// Implement your actor
class CounterActor extends Actor implements Counter {
private count = 0
increment(): void {
this.count++
console.log(`Count: ${this.count}`)
}
decrement(): void {
this.count--
console.log(`Count: ${this.count}`)
}
getValue(): void {
console.log(`Current count: ${this.count}`)
}
}
// Create and use the actor
const counter = stage().actorFor<Counter>(Protocol.typed<Counter>(), CounterActor)
// actorFor() does two things:
//
// 1. actor instance is created and started
// 2. proxy instance is created and returned to caller
counter.increment() // Count: 1
counter.increment() // Count: 2
counter.getValue() // Current count: 2import {
Actor, ActorProtocol, Protocol, stage,
SupervisionStrategy, SupervisionDirective
} from 'domo-actors'
interface Worker extends ActorProtocol {
process(data: string): void
}
class WorkerActor extends Actor implements Worker {
process(data: string): void {
if (data === 'error') {
throw new Error('Processing failed')
}
console.log(`Processed: ${data}`)
}
}
// Custom supervision strategy
class WorkerSupervisor implements SupervisionStrategy {
decide(error: Error): SupervisionDirective {
console.log(`Supervisor handling error: ${error.message}`)
return SupervisionDirective.Restart // Restart the failed actor
}
}
stage().registerSupervisor('worker-supervisor', new WorkerSupervisor())
const worker = stage().actorFor<Worker>(
Protocol.typed<Worker>(),
Worker,
{ supervisorName: 'worker-supervisor' }
)
worker.process('valid') // Processed: valid
worker.process('error') // Supervisor handles error and restarts actorThe Stage Value Registry allows you to register and share runtime objects (like database connections, configuration, or service instances) across all actors in your application:
import { Actor, stage } from 'domo-actors'
// Register shared resources at application startup
const database = new DatabaseConnection('postgresql://localhost:5432/mydb')
const config = { apiKey: 'secret', timeout: 5000 }
stage().registerValue('myapp:database', database)
stage().registerValue('myapp:config', config)
// Access registered values from any actor
class UserRepositoryActor extends Actor {
async findUser(id: string) {
const db = this.stage().registeredValue<DatabaseConnection>('myapp:database')
return await db.query('SELECT * FROM users WHERE id = ?', [id])
}
}
class ApiClientActor extends Actor {
async makeRequest(endpoint: string) {
const config = this.stage().registeredValue<Config>('myapp:config')
// Use config.apiKey and config.timeout
}
}
// Clean up resources when no longer needed
const db = stage().deregisterValue<DatabaseConnection>('myapp:database')
if (db) {
await db.close() // Perform cleanup
}Benefits:
- Dependency Injection: Share dependencies without coupling actors to specific implementations
- Configuration Management: Centralized configuration accessible from any actor
- Resource Pooling: Share connection pools, caches, and other expensive resources
- Type Safety: Full TypeScript type inference with generics
Best Practices:
- Use namespaced keys to avoid conflicts (e.g.,
'myapp:database','myapp:config') - Register values at application startup before creating actors
- Use TypeScript generics for type-safe retrieval
- Use
deregisterValue()to clean up resources when they're no longer needed - Be cautious when deregistering - ensure no actors are still using the value
DomoActors includes comprehensive testing utilities:
import { awaitObservableState, awaitStateValue } from 'domo-actors'
// Wait for observable state changes
await awaitObservableState(observableState, state => state.count === 5)
// Wait for specific state values with timeout
await awaitStateValue(observableState, state => state.status, 'completed', {
timeout: 5000,
interval: 100
})Full API documentation is available at: API Reference
Generate documentation locally:
npm run docs
npm run docs:serveWhen developing DomoActors or running examples from the repository:
# Clone the repository
git clone https://github.com/VaughnVernon/DomoActors-TS.git
cd DomoActors-TS
# Install dependencies
npm install
# Set up local package linking (required for examples)
npm run install:local
# Build the library
npm run build
# Run tests
npm test
# Run examples
npm run example:bank
npm run example:encapsulationNote: The npm run install:local command is required to link the local domo-actors package so that examples can import it. This creates symlinks in node_modules/ that point to your development build.
If you need to remove the local package links:
npm run uninstall:local- Runtimes: Node.js >= 18.0.0, Deno, Bun, Cloudflare Workers, or any V8-based JavaScript runtime
- TypeScript: >= 5.0.0 (for development)
DomoActors has zero Node.js-specific dependencies and runs on any V8-compatible runtime.
DomoActors is authored by Vaughn Vernon and maintained as part of the Domo product family.
For issues and feature requests, please visit: https://github.com/VaughnVernon/DomoActors-TS/issues
Reciprocal Public License 1.5
See: ./LICENSE.md
Copyright © 2012-2025 Vaughn Vernon. All rights reserved. Copyright © 2012-2025 Kalele, Inc. All rights reserved.
Vaughn Vernon
- Creator of the XOOM Platform
- Books:
- Live and In-Person Training:
- LiveLessons video training:
- Domain-Driven Design Distilled
- Available on the O'Reilly Learning Platform
- Strategic Monoliths and Microservices
- Available on the O'Reilly Learning Platform
- Domain-Driven Design Distilled
- Curator and Editor: Pearson Addison-Wesley Signature Series
- Personal website: https://vaughnvernon.com