Simple command bus for node.js
npm install @dkx/command-busSimple command bus for node.js
``bash`
$ npm install --save @dkx/command-bus
or with yarn
`bash`
$ yarn add @dkx/command-bus
Command should be just a simple value object implementing the Command interface.
`typescript
import {Command} from '@dkx/command-bus';
import {User} from './user';
class UpdateUserNameCommand implements Command
{
constructor(
public readonly user: User,
public readonly name: string,
) {}
}
`
There is always one handler for one command and it must implement the Handler interface.
`typescript
import {Handler, Command} from '@dkx/command-bus';
import {User} from './user';
import {UpdateUserNameCommand} from './update-user-name-command';
class UpdateUserNameHandler implements Handler
{
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.
When you have both command and handler, you need to register them in command bus.
`typescript
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:
`typescript`
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.
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.
`typescript
import {Middleware, MiddlewareNextCallback, Command} from '@dkx/command-bus';
class DatabaseTransactionMiddleware implements Middleware
{
public async apply
{
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:
`typescript
import {Middleware, MiddlewareNextCallback, Command} from '@dkx/command-bus';
class LoggingMiddleware implements Middleware
{
public async apply
{
console.log('Running command');
console.log(command);
return next();
}
}
bus.use(new LoggingMiddleware);
``