WIP: Implementation proposition#1
Conversation
b6f8d9b to
44ca126
Compare
src/commandBus.ts
Outdated
| } | ||
|
|
||
| export interface CommandBus { | ||
| handle(command: Command): Promise<any> |
src/middlewares/commandLogger.ts
Outdated
| } | ||
|
|
||
| public async run(command: Command, next: NextMiddleware): Promise<any> { | ||
| const start = Date.now(); |
src/middlewares/commandLogger.ts
Outdated
| throw err; | ||
| } finally { | ||
| const time = Date.now() - start; | ||
| this.logger.log(this.level, 'Command %s time %dms', commandName, time); |
There was a problem hiding this comment.
Do you want to log command two times if error occured?
src/middlewares/index.ts
Outdated
|
|
||
| import { Command } from '../commandBus'; | ||
|
|
||
| export type NextMiddleware = () => Promise<any>; |
There was a problem hiding this comment.
Next middleware should receive command as arg
There was a problem hiding this comment.
Hmm... this was something I was thinking about for a long time...
My conclusion was this: if next() callback would receive command as argument then for most of the time it would be the same command in all meddlewares across the chain invocation. In other words it would be the same command as this passed to run() method.
Now i pass command to next middleware in the background (see for execution chain reducer);
Ok, but maby middleware can change command? Maby middleware creator would create his own command instance and pass this new one to next()?
In my opinion sneaky...
What do You think about this?
There was a problem hiding this comment.
ok, i added command as argument
| const handler = this.handlers.get(command.constructor as ConstructorOf<Command>); | ||
|
|
||
| if (!handler) { | ||
| throw new Error(`Could not resolve handler for ${command.constructor.name}`); |
There was a problem hiding this comment.
imo this exception should come from get()
There was a problem hiding this comment.
Can you explain in more details?
src/resolvers/memory.ts
Outdated
| return this.handlers.size; | ||
| } | ||
|
|
||
| public entries(): IterableIterator<[ConstructorOf<Command>, Handler<Command>]> { |
| export class HandlerResolverInMemory implements HandlerResolver { | ||
| private handlers = new Map<ConstructorOf<Command>, Handler<Command>>(); | ||
|
|
||
| public register<T>(commandConstructor: ConstructorOf<T>, handler: Handler<T>): HandlerResolverInMemory { |
There was a problem hiding this comment.
Why not to get this list by constructor?
There was a problem hiding this comment.
I wanted to be sure that each entry in Map is a command constructor as key and handler instance which handles exacly this particular command as value.
Now it can be checked at compile time for register() method calls.
Have no idea, how to declare handlers property to get same result.
Now propert is declared as any command constructor as value and any handler instane which handles any command.
In other words each call to register() method is made with some generic type which is inferred from command constructor type and this allow compiler to check that handler instance matches that command type.
This prevent mistakes like registering handler for another command.
test/component/commander.test.ts
Outdated
| it("should write info logs", async () => { | ||
| await commander.handle(new HelloCommand('John', 'Doe')); | ||
|
|
||
| const infos = logger.messages.filter(entry => entry.level === 'info'); |
There was a problem hiding this comment.
Are we want to test logger middeware here?
test/fakes/fakeLogger.ts
Outdated
|
|
||
| export class FakeLogger implements CommandLogger { | ||
|
|
||
| public messages: { level: string, msg: string, meta: any }[] = []; |
There was a problem hiding this comment.
if public, can be changed out of scope
There was a problem hiding this comment.
This is also dump implementation for testing. I can use read-only getter like this:
class FakeLogger implements CommandLogger {
// ...
public get Messages() {
return this.messages;
}
}and then in test code:
const infos = logger.Messages.filter(...);What do you think about this?
| import { Middleware, NextMiddleware } from '../../src/middlewares'; | ||
|
|
||
|
|
||
| class CallbackMiddleware implements Middleware { |
There was a problem hiding this comment.
Can exist as provided implementation
There was a problem hiding this comment.
TypeScript do not offer final classes :(
And this class is only for testing.
Hi!
I would like to help create Command Bus library for TypeScript. Any feedback will be appreciated.