@dkx/command-bus
v1.0.0
Published
Simple command bus for node.js
Downloads
7
Readme
DKX/CommandBus
Simple command bus for node.js
Installation
$ npm install --save @dkx/command-bus
or with yarn
$ yarn add @dkx/command-bus
Commands
Command should be just a simple value object implementing the Command
interface.
import {Command} from '@dkx/command-bus';
import {User} from './user';
class UpdateUserNameCommand implements Command
{
constructor(
public readonly user: User,
public readonly name: string,
) {}
}
Handlers
There is always one handler for one command and it must implement the Handler
interface.
import {Handler, Command} from '@dkx/command-bus';
import {User} from './user';
import {UpdateUserNameCommand} from './update-user-name-command';
class UpdateUserNameHandler implements Handler<User>
{
public handle(command: UpdateUserNameCommand): User
{
const user = command.user;
const name = command.name;
// todo: store in DB
user.name = name;
return user;
}
}
The handle
method can return Promise
too.
Usage
When you have both command and handler, you need to register them in command bus.
import {CommandBus, WeakMapMapper} from '@dkx/command-bus';
import {UpdateUserNameCommand} from './update-user-name-command';
import {UpdateUserNameHandler} from './update-user-name-handler';
const mapper = new WeakMapMapper;
mapper.add(UpdateUserNameCommand, () => new UpdateUserNameHandler);
const bus = new CommandBus(mapper);
First argument to add
method is the command itself. The second argument is factory function which must return instance
of the associated handler.
Now you can let the command bus handle the action:
const updatedUser = await bus.handle(new UpdateUserNameCommand(
user,
'John Doe',
));
As you can see, we use the await
here, because handle
method always returns a Promise
.
Middlewares
Middleware can be used to wrap the handle callback into custom method. This is useful for example for logging or maybe
running whole handlers stack in one database transaction. Middleware is class that must implement the Middleware
interface.
The actual handler is called as a last middleware.
import {Middleware, MiddlewareNextCallback, Command} from '@dkx/command-bus';
class DatabaseTransactionMiddleware implements Middleware
{
public async apply<T>(command: Command, next: MiddlewareNextCallback<T>): Promise<T>
{
console.log('Before handler');
let result: T;
await runInTransaction(async () => {
result = await next();
});
console.log('After handler');
return result;
}
}
bus.use(new DatabaseTransactionMiddleware);
apply
method must always return a Promise
and should call the next middleware.
Simple logging middleware could look like this:
import {Middleware, MiddlewareNextCallback, Command} from '@dkx/command-bus';
class LoggingMiddleware implements Middleware
{
public async apply<T>(command: Command, next: MiddlewareNextCallback<T>): Promise<T>
{
console.log('Running command');
console.log(command);
return next();
}
}
bus.use(new LoggingMiddleware);