ts-cqs
v0.1.0
Published
Lightweight Command and Query Separation library
Downloads
8
Maintainers
Readme
ts-cqs
Light Command and Query Separation framework for typescript that uses inversify for dependency injection.
:warning: Note, this is not a CQRS library
Motivation
Following the single responsibility principle, the command and query separation pattern works well. This library sets out to provide a framework in to replicate the CQS pattern in typescript that uses the dependency injection framework InversifyJs.
There are CQRS libraries such as NestJS/CQRS that provides a framework for this pattern. However, it adheres more to the CQRS pattern with event sourcing and uses the bus (command/query processor) as a way to wire up the dependency injection. This library allows you to wire up the dependencies outside of the processors.
Installation
npm install ts-cqs inversify reflect-metadata --save
The InversifyJS type definitions are included in the inversify npm package.
:warning: Important! ts-cqs requires has a peer dependency on InversifyJs, please view their readme for their requirements.
Usage
Commands
// TestCommand.ts
import 'reflect-metadata'
import { injectable } from 'inversify'
import { ICommand, ICommandHandler, CommandHandler } from 'ts-cqs'
export class TestCommand implements ICommand<string> {
constructor(public readonly val: string) {}
}
@injectable()
@CommandHandler(TestCommand)
export class TestCommandHandler implements ICommandHandler<TestCommand> {
async handle(command: TestCommand): Promise<string> {
return command.val
}
}
ICommandHandler.execute
is set to return any
. In this case, command handlers can chose to return something or not.
// CompositeRoot.ts
import { Container } from 'inversify'
import { TestCommandHandler } from './TestCommand'
const container = new Container()
container.bind(TestCommandHandler).toSelf()
The inversify binding here uses the handler class as the id. See docs
// SomeConsumerClass.ts
import { CommandProcessor } from 'ts-cqs'
import { TestCommand } from './TestCommand'
export class SomeConsumerClass {
constructor(private commandProcessor: CommandProcessor) {}
async use(): Promise<string> {
const testCommand = new TestCommand('some value')
const commandResult = await this.commandProcessor.execute(testCommand)
console.log(commandResult) // logs 'some value'
}
}
Contributing
Tests
npm run test