@dkx/command-bus
v1.0.0
Published
Simple command bus for node.js
Readme
DKX/CommandBus
Simple command bus for node.js
Installation
$ npm install --save @dkx/command-busor with yarn
$ yarn add @dkx/command-busCommands
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);