From a2874ee2353de83ef5f6c3c7c0ba98b74c491b55 Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Tue, 9 Jun 2020 00:01:58 +0200 Subject: [PATCH 01/12] Add in memory version of command bus not handling commands --- src/Contexts/Shared/domain/Command.ts | 7 ++++++ src/Contexts/Shared/domain/CommandBus.ts | 5 ++++ .../CommandBus/InMemoryCommandBus.ts | 9 +++++++ .../CommandBus/NoHandlerForMessageError.ts | 7 ++++++ .../CommandBus/InMemoryCommandBus.test.ts | 24 +++++++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 src/Contexts/Shared/domain/Command.ts create mode 100644 src/Contexts/Shared/domain/CommandBus.ts create mode 100644 src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts create mode 100644 src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts create mode 100644 tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts diff --git a/src/Contexts/Shared/domain/Command.ts b/src/Contexts/Shared/domain/Command.ts new file mode 100644 index 0000000..c88751b --- /dev/null +++ b/src/Contexts/Shared/domain/Command.ts @@ -0,0 +1,7 @@ +export abstract class Command { + readonly commandName: string; + + constructor(commandName: string) { + this.commandName = commandName; + } +} \ No newline at end of file diff --git a/src/Contexts/Shared/domain/CommandBus.ts b/src/Contexts/Shared/domain/CommandBus.ts new file mode 100644 index 0000000..82e8096 --- /dev/null +++ b/src/Contexts/Shared/domain/CommandBus.ts @@ -0,0 +1,5 @@ +import { Command } from './Command'; + +export interface CommandBus { + dispatch(command: Command): void; +} \ No newline at end of file diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts new file mode 100644 index 0000000..35d84b9 --- /dev/null +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -0,0 +1,9 @@ +import { CommandBus } from './../../domain/CommandBus'; +import { Command } from '../../domain/Command'; +import { NoHandlerForMessageError } from './NoHandlerForMessageError'; + +export class InMemoryCommandBus implements CommandBus { + dispatch(command: Command): void { + throw new NoHandlerForMessageError(command); + } +} \ No newline at end of file diff --git a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts new file mode 100644 index 0000000..76f34b7 --- /dev/null +++ b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts @@ -0,0 +1,7 @@ +import { Command } from '../../domain/Command'; + +export class NoHandlerForMessageError extends Error { + constructor(command: Command) { + super(`There is not handler for command of type ${command.commandName}`); + } +} \ No newline at end of file diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts new file mode 100644 index 0000000..9a8a25e --- /dev/null +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -0,0 +1,24 @@ +import { InMemoryCommandBus } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus'; +import { Command } from '../../../../../src/Contexts/Shared/domain/Command'; +import { NoHandlerForMessageError } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError'; + +class UnhandledCommand extends Command { + constructor() { + super('UnhandledCommandName'); + } +} + +describe('InMemoryCommandBus', () => { + it('throws an error if dispatches a command without handler', (done) => { + const unhandledCommand = new UnhandledCommand(); + const commandBus = new InMemoryCommandBus(); + + try { + commandBus.dispatch(unhandledCommand); + } catch (error) { + expect(error).toBeInstanceOf(NoHandlerForMessageError); + expect(error.message).toBe('There is not handler for command of type UnhandledCommandName'); + done(); + } + }); +}); From 762b037c131258fcf623981d935eeefe7d9149f5 Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Wed, 10 Jun 2020 17:27:28 +0200 Subject: [PATCH 02/12] WIP --- src/Contexts/Shared/domain/Command.ts | 6 +--- src/Contexts/Shared/domain/CommandHandler.ts | 7 ++++ .../CommandBus/InMemoryCommandBus.ts | 36 +++++++++++++++++-- .../CommandBus/InMemoryCommandBus.test.ts | 27 ++++++++++++-- 4 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 src/Contexts/Shared/domain/CommandHandler.ts diff --git a/src/Contexts/Shared/domain/Command.ts b/src/Contexts/Shared/domain/Command.ts index c88751b..f2c1ef2 100644 --- a/src/Contexts/Shared/domain/Command.ts +++ b/src/Contexts/Shared/domain/Command.ts @@ -1,7 +1,3 @@ export abstract class Command { - readonly commandName: string; - - constructor(commandName: string) { - this.commandName = commandName; - } + static COMMAND_NAME: string; } \ No newline at end of file diff --git a/src/Contexts/Shared/domain/CommandHandler.ts b/src/Contexts/Shared/domain/CommandHandler.ts new file mode 100644 index 0000000..4296bd7 --- /dev/null +++ b/src/Contexts/Shared/domain/CommandHandler.ts @@ -0,0 +1,7 @@ +import { Command } from "./Command"; + +export interface CommandHandler { + subscribedTo(): T; + + handle(command: T): void; +} \ No newline at end of file diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts index 35d84b9..349e2be 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -1,9 +1,41 @@ import { CommandBus } from './../../domain/CommandBus'; import { Command } from '../../domain/Command'; import { NoHandlerForMessageError } from './NoHandlerForMessageError'; +import { CommandHandler } from '../../domain/CommandHandler'; + +export class CommandHandlersInformation { + private commandHandlersMap: Map>; + + constructor(commandHandlers: Array>) { + this.commandHandlersMap = this.formatHandlers(commandHandlers); + } + + private formatHandlers(commandHandlers: Array>) { + const handlersMap = new Map(); + + commandHandlers.forEach(commandHandler => { + handlersMap.set(commandHandler.subscribedTo(), commandHandler); + }); + + return handlersMap; + } + + public getHandler(command: Command) { + return this.commandHandlersMap.get(command.constructor.name); + } +} export class InMemoryCommandBus implements CommandBus { + constructor(private commandHandlersInformation: CommandHandlersInformation) { + } + dispatch(command: Command): void { - throw new NoHandlerForMessageError(command); + const handler = this.commandHandlersInformation.getHandler(command); + + if (!handler) { + throw new NoHandlerForMessageError(command); + } + + handler.handle(command); } -} \ No newline at end of file +} diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts index 9a8a25e..bf55669 100644 --- a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -3,9 +3,19 @@ import { Command } from '../../../../../src/Contexts/Shared/domain/Command'; import { NoHandlerForMessageError } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError'; class UnhandledCommand extends Command { - constructor() { - super('UnhandledCommandName'); - } + static COMMAND_NAME = 'unhandled.command'; +} + +class HandledCommand extends Command { + static COMMAND_NAME = 'handled.command'; +} + +interface CommandHandler { + handle(command: Command): void; +} + +class MyCommandHandler implements CommandHandler { + handle(command: HandledCommand): void {} } describe('InMemoryCommandBus', () => { @@ -21,4 +31,15 @@ describe('InMemoryCommandBus', () => { done(); } }); + + it('accepts a command with handler', done => { + const handledCommand = new HandledCommand(); + const myCommandHandler = new MyCommandHandler(); + const commandBus = new InMemoryCommandBus([myCommandHandler]); + + try { + commandBus.dispatch(handledCommand); + done(); + } catch (error) { } + }); }); From 71a896f8d1fcea6ae63b789a11f87eadce2b1d31 Mon Sep 17 00:00:00 2001 From: rsaladocid Date: Wed, 10 Jun 2020 18:05:56 +0200 Subject: [PATCH 03/12] wip: subscribe to symbol --- src/Contexts/Shared/domain/Command.ts | 4 +-- src/Contexts/Shared/domain/CommandHandler.ts | 9 +++--- .../CommandBus/CommandHandlersInformation.ts | 24 ++++++++++++++ .../CommandBus/InMemoryCommandBus.ts | 31 +++---------------- .../CommandBus/NoHandlerForMessageError.ts | 4 +-- .../CommandBus/InMemoryCommandBus.test.ts | 24 ++++++++------ 6 files changed, 49 insertions(+), 47 deletions(-) create mode 100644 src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts diff --git a/src/Contexts/Shared/domain/Command.ts b/src/Contexts/Shared/domain/Command.ts index f2c1ef2..9074445 100644 --- a/src/Contexts/Shared/domain/Command.ts +++ b/src/Contexts/Shared/domain/Command.ts @@ -1,3 +1 @@ -export abstract class Command { - static COMMAND_NAME: string; -} \ No newline at end of file +export abstract class Command {} diff --git a/src/Contexts/Shared/domain/CommandHandler.ts b/src/Contexts/Shared/domain/CommandHandler.ts index 4296bd7..87d1c94 100644 --- a/src/Contexts/Shared/domain/CommandHandler.ts +++ b/src/Contexts/Shared/domain/CommandHandler.ts @@ -1,7 +1,6 @@ -import { Command } from "./Command"; +import { Command } from './Command'; export interface CommandHandler { - subscribedTo(): T; - - handle(command: T): void; -} \ No newline at end of file + subscribedTo(): T; + handle(command: T): void; +} diff --git a/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts b/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts new file mode 100644 index 0000000..531c872 --- /dev/null +++ b/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts @@ -0,0 +1,24 @@ +import { Command } from '../../domain/Command'; +import { CommandHandler } from '../../domain/CommandHandler'; + +export class CommandHandlersInformation { + private commandHandlersMap: Map>; + + constructor(commandHandlers: Array>) { + this.commandHandlersMap = this.formatHandlers(commandHandlers); + } + + private formatHandlers(commandHandlers: Array>): Map> { + const handlersMap = new Map(); + + commandHandlers.forEach(commandHandler => { + handlersMap.set(commandHandler.subscribedTo(), commandHandler); + }); + + return handlersMap; + } + + public getCommandHandler(command: Command): CommandHandler | undefined { + return this.commandHandlersMap.get(command.constructor); + } +} diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts index 349e2be..fc91594 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -1,36 +1,13 @@ -import { CommandBus } from './../../domain/CommandBus'; import { Command } from '../../domain/Command'; +import { CommandBus } from './../../domain/CommandBus'; +import { CommandHandlersInformation } from './CommandHandlersInformation'; import { NoHandlerForMessageError } from './NoHandlerForMessageError'; -import { CommandHandler } from '../../domain/CommandHandler'; - -export class CommandHandlersInformation { - private commandHandlersMap: Map>; - - constructor(commandHandlers: Array>) { - this.commandHandlersMap = this.formatHandlers(commandHandlers); - } - - private formatHandlers(commandHandlers: Array>) { - const handlersMap = new Map(); - - commandHandlers.forEach(commandHandler => { - handlersMap.set(commandHandler.subscribedTo(), commandHandler); - }); - - return handlersMap; - } - - public getHandler(command: Command) { - return this.commandHandlersMap.get(command.constructor.name); - } -} export class InMemoryCommandBus implements CommandBus { - constructor(private commandHandlersInformation: CommandHandlersInformation) { - } + constructor(private commandHandlersInformation: CommandHandlersInformation) {} dispatch(command: Command): void { - const handler = this.commandHandlersInformation.getHandler(command); + const handler = this.commandHandlersInformation.getCommandHandler(command); if (!handler) { throw new NoHandlerForMessageError(command); diff --git a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts index 76f34b7..4a3d932 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts @@ -2,6 +2,6 @@ import { Command } from '../../domain/Command'; export class NoHandlerForMessageError extends Error { constructor(command: Command) { - super(`There is not handler for command of type ${command.commandName}`); + super(`There is not handler for command of type ${command.constructor.name}`); } -} \ No newline at end of file +} diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts index bf55669..2adc25f 100644 --- a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -1,5 +1,7 @@ -import { InMemoryCommandBus } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus'; import { Command } from '../../../../../src/Contexts/Shared/domain/Command'; +import { CommandHandler } from '../../../../../src/Contexts/Shared/domain/CommandHandler'; +import { CommandHandlersInformation } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation'; +import { InMemoryCommandBus } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus'; import { NoHandlerForMessageError } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError'; class UnhandledCommand extends Command { @@ -10,24 +12,25 @@ class HandledCommand extends Command { static COMMAND_NAME = 'handled.command'; } -interface CommandHandler { - handle(command: Command): void; -} +class MyCommandHandler implements CommandHandler { + subscribedTo(): HandledCommand { + return HandledCommand; + } -class MyCommandHandler implements CommandHandler { handle(command: HandledCommand): void {} } describe('InMemoryCommandBus', () => { - it('throws an error if dispatches a command without handler', (done) => { + it('throws an error if dispatches a command without handler', done => { const unhandledCommand = new UnhandledCommand(); - const commandBus = new InMemoryCommandBus(); + const commandHandlersInformation = new CommandHandlersInformation([]); + const commandBus = new InMemoryCommandBus(commandHandlersInformation); try { commandBus.dispatch(unhandledCommand); } catch (error) { expect(error).toBeInstanceOf(NoHandlerForMessageError); - expect(error.message).toBe('There is not handler for command of type UnhandledCommandName'); + expect(error.message).toBe('There is not handler for command of type UnhandledCommand'); done(); } }); @@ -35,11 +38,12 @@ describe('InMemoryCommandBus', () => { it('accepts a command with handler', done => { const handledCommand = new HandledCommand(); const myCommandHandler = new MyCommandHandler(); - const commandBus = new InMemoryCommandBus([myCommandHandler]); + const commandHandlersInformation = new CommandHandlersInformation([myCommandHandler]); + const commandBus = new InMemoryCommandBus(commandHandlersInformation); try { commandBus.dispatch(handledCommand); done(); - } catch (error) { } + } catch (error) {} }); }); From 3c9c97f9106e91ef30285d399fb35bdcec4bed81 Mon Sep 17 00:00:00 2001 From: rsaladocid Date: Fri, 12 Jun 2020 16:10:18 +0200 Subject: [PATCH 04/12] Refactor command handlers information --- .../Shared/domain/CommandNotRegisteredError.ts | 7 +++++++ .../CommandBus/CommandHandlersInformation.ts | 11 +++++++++-- .../infrastructure/CommandBus/InMemoryCommandBus.ts | 7 +------ .../CommandBus/NoHandlerForMessageError.ts | 7 ------- .../CommandBus/InMemoryCommandBus.test.ts | 6 +++--- 5 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 src/Contexts/Shared/domain/CommandNotRegisteredError.ts delete mode 100644 src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts diff --git a/src/Contexts/Shared/domain/CommandNotRegisteredError.ts b/src/Contexts/Shared/domain/CommandNotRegisteredError.ts new file mode 100644 index 0000000..1da7052 --- /dev/null +++ b/src/Contexts/Shared/domain/CommandNotRegisteredError.ts @@ -0,0 +1,7 @@ +import { Command } from './Command'; + +export class CommandNotRegisteredError extends Error { + constructor(command: Command) { + super(`The command <${command.constructor.name}> hasn't a command handler associated`); + } +} diff --git a/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts b/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts index 531c872..d157ed6 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts @@ -1,5 +1,6 @@ import { Command } from '../../domain/Command'; import { CommandHandler } from '../../domain/CommandHandler'; +import { CommandNotRegisteredError } from '../../domain/CommandNotRegisteredError'; export class CommandHandlersInformation { private commandHandlersMap: Map>; @@ -18,7 +19,13 @@ export class CommandHandlersInformation { return handlersMap; } - public getCommandHandler(command: Command): CommandHandler | undefined { - return this.commandHandlersMap.get(command.constructor); + public search(command: Command): CommandHandler { + const commandHandler = this.commandHandlersMap.get(command.constructor); + + if (!commandHandler) { + throw new CommandNotRegisteredError(command); + } + + return commandHandler; } } diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts index fc91594..949000a 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -1,17 +1,12 @@ import { Command } from '../../domain/Command'; import { CommandBus } from './../../domain/CommandBus'; import { CommandHandlersInformation } from './CommandHandlersInformation'; -import { NoHandlerForMessageError } from './NoHandlerForMessageError'; export class InMemoryCommandBus implements CommandBus { constructor(private commandHandlersInformation: CommandHandlersInformation) {} dispatch(command: Command): void { - const handler = this.commandHandlersInformation.getCommandHandler(command); - - if (!handler) { - throw new NoHandlerForMessageError(command); - } + const handler = this.commandHandlersInformation.search(command); handler.handle(command); } diff --git a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts deleted file mode 100644 index 4a3d932..0000000 --- a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Command } from '../../domain/Command'; - -export class NoHandlerForMessageError extends Error { - constructor(command: Command) { - super(`There is not handler for command of type ${command.constructor.name}`); - } -} diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts index 2adc25f..1de8598 100644 --- a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -1,8 +1,8 @@ import { Command } from '../../../../../src/Contexts/Shared/domain/Command'; import { CommandHandler } from '../../../../../src/Contexts/Shared/domain/CommandHandler'; +import { CommandNotRegisteredError } from '../../../../../src/Contexts/Shared/domain/CommandNotRegisteredError'; import { CommandHandlersInformation } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation'; import { InMemoryCommandBus } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus'; -import { NoHandlerForMessageError } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError'; class UnhandledCommand extends Command { static COMMAND_NAME = 'unhandled.command'; @@ -29,8 +29,8 @@ describe('InMemoryCommandBus', () => { try { commandBus.dispatch(unhandledCommand); } catch (error) { - expect(error).toBeInstanceOf(NoHandlerForMessageError); - expect(error.message).toBe('There is not handler for command of type UnhandledCommand'); + expect(error).toBeInstanceOf(CommandNotRegisteredError); + expect(error.message).toBe(`The command hasn't a command handler associated`); done(); } }); From 784ab218b3f90212f34031b62da3e36e12b8211a Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Fri, 12 Jun 2020 17:29:01 +0200 Subject: [PATCH 05/12] Command handler to create course MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rubén Salado Co-authored-by: Iván Portillo --- .../Mooc/Courses/application/CourseCreator.ts | 14 ++++++++---- .../application/CreateCourseCommand.ts | 20 +++++++++++++++++ .../application/CreateCourseCommandHandler.ts | 22 +++++++++++++++++++ src/Contexts/Shared/domain/CommandBus.ts | 2 +- src/Contexts/Shared/domain/CommandHandler.ts | 4 ++-- .../CommandBus/InMemoryCommandBus.ts | 4 ++-- .../Courses/application.yaml | 6 +++++ .../Shared/application.yaml | 8 +++++++ .../apps/application.yaml | 2 +- .../controllers/CoursePutController.ts | 8 ++++--- 10 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts create mode 100644 src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler.ts diff --git a/src/Contexts/Mooc/Courses/application/CourseCreator.ts b/src/Contexts/Mooc/Courses/application/CourseCreator.ts index 67acc99..0880cdf 100644 --- a/src/Contexts/Mooc/Courses/application/CourseCreator.ts +++ b/src/Contexts/Mooc/Courses/application/CourseCreator.ts @@ -6,6 +6,12 @@ import { CourseName } from '../domain/CourseName'; import { CourseDuration } from '../domain/CourseDuration'; import { EventBus } from '../../../Shared/domain/EventBus'; +type Params = { + courseId: CourseId; + courseName: CourseName; + courseDuration: CourseDuration; +}; + export class CourseCreator { private repository: CourseRepository; private eventBus: EventBus; @@ -15,11 +21,11 @@ export class CourseCreator { this.eventBus = eventBus; } - async run(request: CreateCourseRequest): Promise { + async run({ courseId, courseName, courseDuration }: Params): Promise { const course = Course.create( - new CourseId(request.id), - new CourseName(request.name), - new CourseDuration(request.duration) + courseId, + courseName, + courseDuration ); await this.repository.save(course); diff --git a/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts b/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts new file mode 100644 index 0000000..199f777 --- /dev/null +++ b/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts @@ -0,0 +1,20 @@ +import { Command } from '../../../Shared/domain/Command'; + +type Params = { + id: string; + name: string; + duration: string; +}; + +export class CreateCourseCommand extends Command { + id: string; + name: string; + duration: string; + + constructor({ id, name, duration }: Params) { + super(); + this.id = id; + this.name = name; + this.duration = duration; + } +} \ No newline at end of file diff --git a/src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler.ts b/src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler.ts new file mode 100644 index 0000000..2c8c60e --- /dev/null +++ b/src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler.ts @@ -0,0 +1,22 @@ +import { CreateCourseCommand } from './CreateCourseCommand'; +import { CommandHandler } from '../../../Shared/domain/CommandHandler'; +import { CourseCreator } from './CourseCreator'; +import { Command } from '../../../Shared/domain/Command'; +import { CourseId } from '../../Shared/domain/Courses/CourseId'; +import { CourseName } from '../domain/CourseName'; +import { CourseDuration } from '../domain/CourseDuration'; + +export class CreateCourseCommandHandler implements CommandHandler { + constructor(private courseCreator: CourseCreator) {} + + subscribedTo(): Command { + return CreateCourseCommand; + } + + async handle(command: CreateCourseCommand): Promise { + const courseId = new CourseId(command.id); + const courseName = new CourseName(command.name); + const courseDuration = new CourseDuration(command.duration); + await this.courseCreator.run({ courseId, courseName, courseDuration }); + } +} diff --git a/src/Contexts/Shared/domain/CommandBus.ts b/src/Contexts/Shared/domain/CommandBus.ts index 82e8096..a4aebbc 100644 --- a/src/Contexts/Shared/domain/CommandBus.ts +++ b/src/Contexts/Shared/domain/CommandBus.ts @@ -1,5 +1,5 @@ import { Command } from './Command'; export interface CommandBus { - dispatch(command: Command): void; + dispatch(command: Command): Promise; } \ No newline at end of file diff --git a/src/Contexts/Shared/domain/CommandHandler.ts b/src/Contexts/Shared/domain/CommandHandler.ts index 87d1c94..65be088 100644 --- a/src/Contexts/Shared/domain/CommandHandler.ts +++ b/src/Contexts/Shared/domain/CommandHandler.ts @@ -1,6 +1,6 @@ import { Command } from './Command'; export interface CommandHandler { - subscribedTo(): T; - handle(command: T): void; + subscribedTo(): Command; + handle(command: T): Promise; } diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts index 949000a..011bdb2 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -5,9 +5,9 @@ import { CommandHandlersInformation } from './CommandHandlersInformation'; export class InMemoryCommandBus implements CommandBus { constructor(private commandHandlersInformation: CommandHandlersInformation) {} - dispatch(command: Command): void { + async dispatch(command: Command): Promise { const handler = this.commandHandlersInformation.search(command); - handler.handle(command); + await handler.handle(command); } } diff --git a/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml index c4abfad..8acb39e 100644 --- a/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml @@ -7,3 +7,9 @@ services: Mooc.courses.CourseCreator: class: ../../../../../Contexts/Mooc/Courses/application/CourseCreator arguments: ['@Mooc.courses.CourseRepository', '@Mooc.shared.EventBus'] + + Mooc.courses.CreateCourseCommandHandler: + class: ../../../../../Contexts/Mooc/Courses/application/CreateCourseCommandHandler + arguments: ['@Mooc.courses.CourseCreator'] + tags: + - { name: 'commandHandler' } \ No newline at end of file diff --git a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml index 62137a3..51165b3 100644 --- a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml @@ -13,6 +13,14 @@ services: class: ../../../../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus arguments: [] + Mooc.shared.CommandHandlersInformation: + class: ../../../../../Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation + arguments: ['!tagged commandHandler'] + + Mooc.shared.CommandBus: + class: ../../../../../Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus + arguments: ['@Mooc.shared.CommandHandlersInformation'] + Mooc.shared.EventBus.DomainEventMapping: class: ../../../../../Contexts/Shared/infrastructure/EventBus/DomainEventMapping arguments: ['!tagged domainEventSubscriber'] diff --git a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml index 308a6f9..3e4dff9 100644 --- a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml @@ -2,7 +2,7 @@ services: Apps.mooc.controllers.CoursePutController: class: ../../../controllers/CoursePutController - arguments: ["@Mooc.courses.CourseCreator"] + arguments: ["@Mooc.shared.CommandBus"] Apps.mooc.controllers.StatusGetController: class: ../../../controllers/StatusGetController diff --git a/src/apps/mooc_backend/controllers/CoursePutController.ts b/src/apps/mooc_backend/controllers/CoursePutController.ts index 4b1724e..1863ab7 100644 --- a/src/apps/mooc_backend/controllers/CoursePutController.ts +++ b/src/apps/mooc_backend/controllers/CoursePutController.ts @@ -1,19 +1,21 @@ import { Request, Response } from 'express'; -import { CourseCreator } from '../../../Contexts/Mooc/Courses/application/CourseCreator'; import httpStatus from 'http-status'; import { Controller } from './Controller'; import { CourseAlreadyExists } from '../../../Contexts/Mooc/Courses/domain/CourseAlreadyExists'; +import { CommandBus } from '../../../Contexts/Shared/domain/CommandBus'; +import { CreateCourseCommand } from '../../../Contexts/Mooc/Courses/application/CreateCourseCommand'; export class CoursePutController implements Controller { - constructor(private courseCreator: CourseCreator) {} + constructor(private commandBus: CommandBus) {} async run(req: Request, res: Response) { const id: string = req.params.id; const name: string = req.body.name; const duration: string = req.body.duration; + const createCourseCommand = new CreateCourseCommand({ id, name, duration }); try { - await this.courseCreator.run({ id, name, duration }); + await this.commandBus.dispatch(createCourseCommand); } catch (error) { if (error instanceof CourseAlreadyExists) { res.status(httpStatus.BAD_REQUEST).send(error.message); From 2543df4202d570d3edadf907d601ed2873de8912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Thu, 18 Jun 2020 17:26:56 +0200 Subject: [PATCH 06/12] Refactor CourseCreator test to use command handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aliondev Co-authored-by: Rubén Salado --- .../Courses/application/CourseCreator.test.ts | 16 ++++++++-------- .../application/CreateCourseCommandMother.ts | 14 ++++++++++++++ .../application/CreateCourseRequestMother.ts | 17 ----------------- .../Contexts/Mooc/Courses/domain/Course.test.ts | 12 ++++++------ .../Mooc/Courses/domain/CourseMother.ts | 9 +++++---- 5 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 tests/Contexts/Mooc/Courses/application/CreateCourseCommandMother.ts delete mode 100644 tests/Contexts/Mooc/Courses/application/CreateCourseRequestMother.ts diff --git a/tests/Contexts/Mooc/Courses/application/CourseCreator.test.ts b/tests/Contexts/Mooc/Courses/application/CourseCreator.test.ts index 3609c2c..a406009 100644 --- a/tests/Contexts/Mooc/Courses/application/CourseCreator.test.ts +++ b/tests/Contexts/Mooc/Courses/application/CourseCreator.test.ts @@ -1,25 +1,25 @@ import { CourseCreator } from '../../../../../src/Contexts/Mooc/Courses/application/CourseCreator'; import { CourseMother } from '../domain/CourseMother'; import { CourseRepositoryMock } from '../__mocks__/CourseRepositoryMock'; -import { CreateCourseRequestMother } from './CreateCourseRequestMother'; +import { CreateCourseCommandMother } from './CreateCourseCommandMother'; import EventBusMock from '../__mocks__/EventBusMock'; +import { CreateCourseCommandHandler } from '../../../../../src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler'; let repository: CourseRepositoryMock; -let creator: CourseCreator; +let handler: CreateCourseCommandHandler; const eventBus = new EventBusMock(); beforeEach(() => { repository = new CourseRepositoryMock(); - creator = new CourseCreator(repository, eventBus); + const creator = new CourseCreator(repository, eventBus); + handler = new CreateCourseCommandHandler(creator); }); it('should create a valid course', async () => { - const request = CreateCourseRequestMother.random(); - - const course = CourseMother.fromRequest(request); - - await creator.run(request); + const command = CreateCourseCommandMother.random(); + await handler.handle(command); + const course = CourseMother.fromCommand(command); repository.assertLastSavedCourseIs(course); }); diff --git a/tests/Contexts/Mooc/Courses/application/CreateCourseCommandMother.ts b/tests/Contexts/Mooc/Courses/application/CreateCourseCommandMother.ts new file mode 100644 index 0000000..02b796d --- /dev/null +++ b/tests/Contexts/Mooc/Courses/application/CreateCourseCommandMother.ts @@ -0,0 +1,14 @@ +import { CourseDurationMother } from '../domain/CourseDurationMother'; +import { CourseIdMother } from '../../Shared/domain/Courses/CourseIdMother'; +import { CourseNameMother } from '../domain/CourseNameMother'; +import { CreateCourseCommand } from '../../../../../src/Contexts/Mooc/Courses/application/CreateCourseCommand'; + +export class CreateCourseCommandMother { + static create(id: string, name: string, duration: string): CreateCourseCommand { + return new CreateCourseCommand({ id, name, duration }); + } + + static random(): CreateCourseCommand { + return this.create(CourseIdMother.random().value, CourseNameMother.random().value, CourseDurationMother.random().value); + } +} diff --git a/tests/Contexts/Mooc/Courses/application/CreateCourseRequestMother.ts b/tests/Contexts/Mooc/Courses/application/CreateCourseRequestMother.ts deleted file mode 100644 index d780d22..0000000 --- a/tests/Contexts/Mooc/Courses/application/CreateCourseRequestMother.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CreateCourseRequest } from '../../../../../src/Contexts/Mooc/Courses/application/CreateCourseRequest'; -import { CourseDuration } from '../../../../../src/Contexts/Mooc/Courses/domain/CourseDuration'; -import { CourseId } from '../../../../../src/Contexts/Mooc/Shared/domain/Courses/CourseId'; -import { CourseName } from '../../../../../src/Contexts/Mooc/Courses/domain/CourseName'; -import { CourseDurationMother } from '../domain/CourseDurationMother'; -import { CourseIdMother } from '../../Shared/domain/Courses/CourseIdMother'; -import { CourseNameMother } from '../domain/CourseNameMother'; - -export class CreateCourseRequestMother { - static create(id: CourseId, name: CourseName, duration: CourseDuration): CreateCourseRequest { - return { id: id.value, name: name.value, duration: duration.value }; - } - - static random(): CreateCourseRequest { - return this.create(CourseIdMother.random(), CourseNameMother.random(), CourseDurationMother.random()); - } -} diff --git a/tests/Contexts/Mooc/Courses/domain/Course.test.ts b/tests/Contexts/Mooc/Courses/domain/Course.test.ts index f6f65fd..cdd67c1 100644 --- a/tests/Contexts/Mooc/Courses/domain/Course.test.ts +++ b/tests/Contexts/Mooc/Courses/domain/Course.test.ts @@ -1,4 +1,4 @@ -import { CreateCourseRequestMother } from '../application/CreateCourseRequestMother'; +import { CreateCourseCommandMother } from '../application/CreateCourseCommandMother'; import { CourseMother } from './CourseMother'; import { Course } from '../../../../../src/Contexts/Mooc/Courses/domain/Course'; import { CourseIdMother } from '../../Shared/domain/Courses/CourseIdMother'; @@ -8,13 +8,13 @@ import { CourseDurationMother } from './CourseDurationMother'; describe('Course', () => { it('should return a new course instance', () => { - const request = CreateCourseRequestMother.random(); + const command = CreateCourseCommandMother.random(); - const course = CourseMother.fromRequest(request); + const course = CourseMother.fromCommand(command); - expect(course.id.value).toBe(request.id); - expect(course.name.value).toBe(request.name); - expect(course.duration.value).toBe(request.duration); + expect(course.id.value).toBe(command.id); + expect(course.name.value).toBe(command.name); + expect(course.duration.value).toBe(command.duration); }); it('should record a CourseCreatedDomainEvent after its creation', () => { diff --git a/tests/Contexts/Mooc/Courses/domain/CourseMother.ts b/tests/Contexts/Mooc/Courses/domain/CourseMother.ts index 5d53285..4c388be 100644 --- a/tests/Contexts/Mooc/Courses/domain/CourseMother.ts +++ b/tests/Contexts/Mooc/Courses/domain/CourseMother.ts @@ -6,17 +6,18 @@ import { CreateCourseRequest } from '../../../../../src/Contexts/Mooc/Courses/ap import { CourseIdMother } from '../../Shared/domain/Courses/CourseIdMother'; import { CourseNameMother } from './CourseNameMother'; import { CourseDurationMother } from './CourseDurationMother'; +import { CreateCourseCommand } from '../../../../../src/Contexts/Mooc/Courses/application/CreateCourseCommand'; export class CourseMother { static create(id: CourseId, name: CourseName, duration: CourseDuration): Course { return new Course(id, name, duration); } - static fromRequest(request: CreateCourseRequest): Course { + static fromCommand(command: CreateCourseCommand): Course { return this.create( - CourseIdMother.create(request.id), - CourseNameMother.create(request.name), - CourseDurationMother.create(request.duration) + CourseIdMother.create(command.id), + CourseNameMother.create(command.name), + CourseDurationMother.create(command.duration) ); } From 3ad53bc9ee22cff6bfbcce52b5351c2b5df89e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Thu, 18 Jun 2020 17:36:58 +0200 Subject: [PATCH 07/12] Fix InMemoryCommandBus test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aliondev Co-authored-by: Rubén Salado --- .../CommandBus/InMemoryCommandBus.test.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts index 1de8598..141bfa3 100644 --- a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -17,33 +17,33 @@ class MyCommandHandler implements CommandHandler { return HandledCommand; } - handle(command: HandledCommand): void {} + async handle(command: HandledCommand): Promise {} } describe('InMemoryCommandBus', () => { - it('throws an error if dispatches a command without handler', done => { + it('throws an error if dispatches a command without handler', async () => { const unhandledCommand = new UnhandledCommand(); const commandHandlersInformation = new CommandHandlersInformation([]); const commandBus = new InMemoryCommandBus(commandHandlersInformation); + let exception = null; + try { - commandBus.dispatch(unhandledCommand); + await commandBus.dispatch(unhandledCommand); } catch (error) { - expect(error).toBeInstanceOf(CommandNotRegisteredError); - expect(error.message).toBe(`The command hasn't a command handler associated`); - done(); + exception = error; } + + expect(exception).toBeInstanceOf(CommandNotRegisteredError); + expect(exception.message).toBe(`The command hasn't a command handler associated`); }); - it('accepts a command with handler', done => { + it('accepts a command with handler', async () => { const handledCommand = new HandledCommand(); const myCommandHandler = new MyCommandHandler(); const commandHandlersInformation = new CommandHandlersInformation([myCommandHandler]); const commandBus = new InMemoryCommandBus(commandHandlersInformation); - try { - commandBus.dispatch(handledCommand); - done(); - } catch (error) {} + await commandBus.dispatch(handledCommand); }); }); From a69946dbf1d96990cb6f3724fdf5afc662118f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Thu, 18 Jun 2020 18:26:46 +0200 Subject: [PATCH 08/12] Add query bus (WIP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aliondev Co-authored-by: Rubén Salado --- src/Contexts/Shared/domain/Query.ts | 1 + src/Contexts/Shared/domain/QueryBus.ts | 5 ++ src/Contexts/Shared/domain/QueryHandler.ts | 7 +++ .../Shared/domain/QueryNotRegisteredError.ts | 7 +++ src/Contexts/Shared/domain/Response.ts | 1 + .../QueryBus/QueryHandlersInformation.ts | 32 ++++++++++++ .../CoursesCounterGetController.ts | 3 ++ .../QueryBus/InMemoryQueryBus.test.ts | 49 +++++++++++++++++++ 8 files changed, 105 insertions(+) create mode 100644 src/Contexts/Shared/domain/Query.ts create mode 100644 src/Contexts/Shared/domain/QueryBus.ts create mode 100644 src/Contexts/Shared/domain/QueryHandler.ts create mode 100644 src/Contexts/Shared/domain/QueryNotRegisteredError.ts create mode 100644 src/Contexts/Shared/domain/Response.ts create mode 100644 src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts create mode 100644 tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts diff --git a/src/Contexts/Shared/domain/Query.ts b/src/Contexts/Shared/domain/Query.ts new file mode 100644 index 0000000..1acca03 --- /dev/null +++ b/src/Contexts/Shared/domain/Query.ts @@ -0,0 +1 @@ +export abstract class Query {} diff --git a/src/Contexts/Shared/domain/QueryBus.ts b/src/Contexts/Shared/domain/QueryBus.ts new file mode 100644 index 0000000..54b7b57 --- /dev/null +++ b/src/Contexts/Shared/domain/QueryBus.ts @@ -0,0 +1,5 @@ +import { Query } from './Query'; + +export interface QueryBus { + ask(query: Query): Promise; +} \ No newline at end of file diff --git a/src/Contexts/Shared/domain/QueryHandler.ts b/src/Contexts/Shared/domain/QueryHandler.ts new file mode 100644 index 0000000..49574f8 --- /dev/null +++ b/src/Contexts/Shared/domain/QueryHandler.ts @@ -0,0 +1,7 @@ +import { Query } from './Query'; +import { Response } from './Response'; + +export interface QueryHandler { + subscribedTo(): Query; + handle(query: T): Promise; +} diff --git a/src/Contexts/Shared/domain/QueryNotRegisteredError.ts b/src/Contexts/Shared/domain/QueryNotRegisteredError.ts new file mode 100644 index 0000000..aa26fc4 --- /dev/null +++ b/src/Contexts/Shared/domain/QueryNotRegisteredError.ts @@ -0,0 +1,7 @@ +import { Query } from './Query'; + +export class QueryNotRegisteredError extends Error { + constructor(query: Query) { + super(`The query <${query.constructor.name}> hasn't a query handler associated`); + } +} diff --git a/src/Contexts/Shared/domain/Response.ts b/src/Contexts/Shared/domain/Response.ts new file mode 100644 index 0000000..c1db157 --- /dev/null +++ b/src/Contexts/Shared/domain/Response.ts @@ -0,0 +1 @@ +export interface Response {} \ No newline at end of file diff --git a/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts b/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts new file mode 100644 index 0000000..e751eca --- /dev/null +++ b/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts @@ -0,0 +1,32 @@ +import { Query } from '../../domain/Query'; +import { QueryHandler } from '../../domain/QueryHandler'; +import { Response } from '../../domain/Response'; +import { QueryNotRegisteredError } from '../../domain/QueryNotRegisteredError'; + +export class QueryHandlersInformation { + private queryHandlersMap: Map>; + + constructor(queryHandlers: Array>) { + this.queryHandlersMap = this.formatHandlers(queryHandlers); + } + + private formatHandlers(queryHandlers: Array>): Map> { + const handlersMap = new Map(); + + queryHandlers.forEach(queryHandler => { + handlersMap.set(queryHandler.subscribedTo(), queryHandler); + }); + + return handlersMap; + } + + public search(query: Query): QueryHandler { + const queryHandler = this.queryHandlersMap.get(query.constructor); + + if (!queryHandler) { + throw new QueryNotRegisteredError(query); + } + + return queryHandler; + } +} diff --git a/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts b/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts index 088faeb..175364b 100644 --- a/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts +++ b/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts @@ -8,6 +8,9 @@ export class CoursesCounterGetController implements Controller { constructor(private coursesCounterFinder: CoursesCounterFinder) {} async run(req: Request, res: Response): Promise { try { + const query = new FindCourseCounterQuery(); + const count = await this.queryBus.query(query); + const counter = await this.coursesCounterFinder.run(); res.status(httpStatus.OK).send(counter); } catch (e) { diff --git a/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts b/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts new file mode 100644 index 0000000..eb96306 --- /dev/null +++ b/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts @@ -0,0 +1,49 @@ +import { Query } from '../../../../../src/Contexts/Shared/domain/Query'; +import { QueryHandlersInformation } from '../../../../../src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation'; +import { QueryNotRegisteredError } from '../../../../../src/Contexts/Shared/domain/QueryNotRegisteredError'; +import { QueryHandler } from '../../../../../src/Contexts/Shared/domain/QueryHandler'; +import { Response } from '../../../../../src/Contexts/Shared/domain/Response'; + +class UnhandledQuery extends Query { + static QUERY_NAME = 'unhandled.query'; +} + +class HandledQuery extends Query { + static QUERY_NAME = 'handled.query'; +} + +class MyQueryHandler implements QueryHandler { + subscribedTo(): HandledQuery { + return HandledQuery; + } + + async handle(query: HandledQuery): Promise {return {};} +} + +describe('InMemoryQueryBus', () => { + it('throws an error if dispatches a query without handler', async () => { + const unhandledQuery = new UnhandledQuery(); + const queryHandlersInformation = new QueryHandlersInformation([]); + const queryBus = new InMemoryQueryBus(queryHandlersInformation); + + let exception = null; + + try { + await queryBus.ask(unhandledQuery); + } catch (error) { + exception = error; + } + + expect(exception).toBeInstanceOf(QueryNotRegisteredError); + expect(exception.message).toBe(`The query hasn't a query handler associated`); + }); + + it('accepts a query with handler', async () => { + const handledQuery = new HandledQuery(); + const myQueryHandler = new MyQueryHandler(); + const queryHandlersInformation = new QueryHandlersInformation([myQueryHandler]); + const queryBus = new InMemoryQueryBus(queryHandlersInformation); + + await queryBus.ask(handledQuery); + }); +}); From 69f44d604f5d6cfdfb69ad9582689783b924d28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Wed, 24 Jun 2020 17:22:23 +0200 Subject: [PATCH 09/12] Implement InMemoryQueryBus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rubén Salado Co-authored-by: aliondev --- src/Contexts/Shared/domain/QueryBus.ts | 3 ++- src/Contexts/Shared/domain/QueryHandler.ts | 4 ++-- .../infrastructure/QueryBus/InMemoryQueryBus.ts | 14 ++++++++++++++ .../QueryBus/InMemoryQueryBus.test.ts | 3 ++- 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.ts diff --git a/src/Contexts/Shared/domain/QueryBus.ts b/src/Contexts/Shared/domain/QueryBus.ts index 54b7b57..2a04f03 100644 --- a/src/Contexts/Shared/domain/QueryBus.ts +++ b/src/Contexts/Shared/domain/QueryBus.ts @@ -1,5 +1,6 @@ import { Query } from './Query'; +import { Response } from './Response'; export interface QueryBus { - ask(query: Query): Promise; + ask(query: Query): Promise; } \ No newline at end of file diff --git a/src/Contexts/Shared/domain/QueryHandler.ts b/src/Contexts/Shared/domain/QueryHandler.ts index 49574f8..a988d70 100644 --- a/src/Contexts/Shared/domain/QueryHandler.ts +++ b/src/Contexts/Shared/domain/QueryHandler.ts @@ -1,7 +1,7 @@ import { Query } from './Query'; import { Response } from './Response'; -export interface QueryHandler { +export interface QueryHandler { subscribedTo(): Query; - handle(query: T): Promise; + handle(query: Q): Promise; } diff --git a/src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.ts b/src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.ts new file mode 100644 index 0000000..ad7cbe6 --- /dev/null +++ b/src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.ts @@ -0,0 +1,14 @@ +import { Query } from '../../domain/Query'; +import { Response } from '../../domain/Response'; +import { QueryBus } from './../../domain/QueryBus'; +import { QueryHandlersInformation } from './QueryHandlersInformation'; + +export class InMemoryQueryBus implements QueryBus { + constructor(private queryHandlersInformation: QueryHandlersInformation) {} + + async ask(query: Query): Promise { + const handler = this.queryHandlersInformation.search(query); + + return handler.handle(query) as Promise; + } +} diff --git a/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts b/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts index eb96306..829e1c5 100644 --- a/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts @@ -3,6 +3,7 @@ import { QueryHandlersInformation } from '../../../../../src/Contexts/Shared/inf import { QueryNotRegisteredError } from '../../../../../src/Contexts/Shared/domain/QueryNotRegisteredError'; import { QueryHandler } from '../../../../../src/Contexts/Shared/domain/QueryHandler'; import { Response } from '../../../../../src/Contexts/Shared/domain/Response'; +import { InMemoryQueryBus } from '../../../../../src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus'; class UnhandledQuery extends Query { static QUERY_NAME = 'unhandled.query'; @@ -35,7 +36,7 @@ describe('InMemoryQueryBus', () => { } expect(exception).toBeInstanceOf(QueryNotRegisteredError); - expect(exception.message).toBe(`The query hasn't a query handler associated`); + expect(exception.message).toBe(`The query hasn't a query handler associated`); }); it('accepts a query with handler', async () => { From d6b7729e96f7ef64681101151e794f38913ed9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Wed, 24 Jun 2020 17:54:52 +0200 Subject: [PATCH 10/12] Refactor FindCourseCounter to use query bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rubén Salado Co-authored-by: aliondev --- .../application/Find/CoursesCounterFinder.ts | 4 +-- .../Find/FindCoursesCounterQuery.ts | 3 +++ .../Find/FindCoursesCounterQueryHandler.ts | 17 +++++++++++++ ...ponse.ts => FindCoursesCounterResponse.ts} | 2 +- .../CoursesCounter/application.yaml | 7 +++++- .../Shared/application.yaml | 8 ++++++ .../apps/application.yaml | 2 +- .../CoursesCounterGetController.ts | 13 +++++----- .../__mocks__/CoursesCounterRepositoryMock.ts | 1 - ... => FindCourseCounterQueryHandler.test.ts} | 25 +++++++++++++------ .../domain/CoursesCounterResponseMother.ts | 4 +-- 11 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts create mode 100644 src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts rename src/Contexts/Mooc/CoursesCounter/application/Find/{CoursesCounterResponse.ts => FindCoursesCounterResponse.ts} (67%) rename tests/Contexts/Mooc/CoursesCounter/application/Find/{CoursesCounterFinder.test.ts => FindCourseCounterQueryHandler.test.ts} (55%) diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.ts index 6f95bcb..445fde7 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.ts @@ -1,6 +1,6 @@ import { CoursesCounterRepository } from '../../domain/CoursesCounterRepository'; import { CoursesCounterNotExist } from '../../domain/CoursesCounterNotExist'; -import { CoursesCounterResponse } from './CoursesCounterResponse'; +import { FindCoursesCounterResponse } from './FindCoursesCounterResponse'; export class CoursesCounterFinder { constructor(private repository: CoursesCounterRepository) {} @@ -11,6 +11,6 @@ export class CoursesCounterFinder { throw new CoursesCounterNotExist(); } - return new CoursesCounterResponse(counter.total.value); + return new FindCoursesCounterResponse(counter.total.value); } } diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts new file mode 100644 index 0000000..312dd47 --- /dev/null +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts @@ -0,0 +1,3 @@ +import { Query } from "../../../../Shared/domain/Query"; + +export class FindCoursesCounterQuery implements Query {} \ No newline at end of file diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts new file mode 100644 index 0000000..c8e85ab --- /dev/null +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts @@ -0,0 +1,17 @@ +import { QueryHandler } from "../../../../Shared/domain/QueryHandler"; +import { FindCoursesCounterQuery } from "./FindCoursesCounterQuery"; +import { FindCoursesCounterResponse } from "./FindCoursesCounterResponse"; +import { Query } from "../../../../Shared/domain/Query"; +import { CoursesCounterFinder } from "./CoursesCounterFinder"; + +export class FindCoursesCounterQueryHandler implements QueryHandler { + constructor(private finder: CoursesCounterFinder) {} + + subscribedTo(): Query { + return FindCoursesCounterQuery; + } + handle(_query: FindCoursesCounterQuery): Promise { + return this.finder.run(); + } + +} \ No newline at end of file diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterResponse.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse.ts similarity index 67% rename from src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterResponse.ts rename to src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse.ts index af89a06..fa5bab2 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterResponse.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse.ts @@ -1,4 +1,4 @@ -export class CoursesCounterResponse { +export class FindCoursesCounterResponse { readonly total: number; constructor(total: number) { diff --git a/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml b/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml index 8314388..493864c 100644 --- a/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml @@ -1,5 +1,4 @@ services: - Mooc.coursesCounter.CoursesCounterRepository: class: ../../../../../Contexts/Mooc/CoursesCounter/infrastructure/persistence/mongo/MongoCoursesCounterRepository arguments: ["@Mooc.shared.ConnectionManager"] @@ -21,4 +20,10 @@ services: class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder arguments: ["@Mooc.coursesCounter.CoursesCounterRepository"] + + Mooc.coursesCounter.FindCoursesCounterQueryHandler: + class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler + arguments: ["@Mooc.coursesCounter.CoursesCounterFinder"] + tags: + - { name: 'queryHandler' } diff --git a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml index 51165b3..624a233 100644 --- a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml @@ -28,3 +28,11 @@ services: Mooc.shared.EventBus.DomainEventJsonDeserializer: class: ../../../../../Contexts/Shared/infrastructure/EventBus/DomainEventJsonDeserializer arguments: ['@Mooc.shared.EventBus.DomainEventMapping'] + + Mooc.shared.QueryHandlersInformation: + class: ../../../../../Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation + arguments: ['!tagged queryHandler'] + + Mooc.shared.QueryBus: + class: ../../../../../Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus + arguments: ['@Mooc.shared.QueryHandlersInformation'] diff --git a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml index 3e4dff9..131ee62 100644 --- a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml @@ -10,4 +10,4 @@ services: Apps.mooc.controllers.CoursesCounterGetController: class: ../../../controllers/CoursesCounterGetController - arguments: ["@Mooc.coursesCounter.CoursesCounterFinder"] + arguments: ["@Mooc.shared.QueryBus"] diff --git a/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts b/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts index 175364b..9f97752 100644 --- a/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts +++ b/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts @@ -1,18 +1,19 @@ import { Controller } from './Controller'; import { Request, Response } from 'express'; -import { CoursesCounterFinder } from '../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder'; import httpStatus = require('http-status'); import { CoursesCounterNotExist } from '../../../Contexts/Mooc/CoursesCounter/domain/CoursesCounterNotExist'; +import { FindCoursesCounterQuery } from '../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery'; +import { QueryBus } from '../../../Contexts/Shared/domain/QueryBus'; +import { FindCoursesCounterResponse } from '../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse'; export class CoursesCounterGetController implements Controller { - constructor(private coursesCounterFinder: CoursesCounterFinder) {} + constructor(private queryBus: QueryBus) {} async run(req: Request, res: Response): Promise { try { - const query = new FindCourseCounterQuery(); - const count = await this.queryBus.query(query); + const query = new FindCoursesCounterQuery(); + const count = await this.queryBus.ask(query); - const counter = await this.coursesCounterFinder.run(); - res.status(httpStatus.OK).send(counter); + res.status(httpStatus.OK).send(count); } catch (e) { if (e instanceof CoursesCounterNotExist) { res.status(httpStatus.NOT_FOUND).send(); diff --git a/tests/Contexts/Mooc/CoursesCounter/__mocks__/CoursesCounterRepositoryMock.ts b/tests/Contexts/Mooc/CoursesCounter/__mocks__/CoursesCounterRepositoryMock.ts index cd88fe5..15bf9a0 100644 --- a/tests/Contexts/Mooc/CoursesCounter/__mocks__/CoursesCounterRepositoryMock.ts +++ b/tests/Contexts/Mooc/CoursesCounter/__mocks__/CoursesCounterRepositoryMock.ts @@ -1,7 +1,6 @@ import { CoursesCounterRepository } from '../../../../../src/Contexts/Mooc/CoursesCounter/domain/CoursesCounterRepository'; import { CoursesCounter } from '../../../../../src/Contexts/Mooc/CoursesCounter/domain/CoursesCounter'; import { Nullable } from '../../../../../src/Contexts/Shared/domain/Nullable'; -import { CourseId } from '../../../../../src/Contexts/Mooc/Shared/domain/Courses/CourseId'; export class CoursesCounterRepositoryMock implements CoursesCounterRepository { private mockSave = jest.fn(); diff --git a/tests/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.test.ts b/tests/Contexts/Mooc/CoursesCounter/application/Find/FindCourseCounterQueryHandler.test.ts similarity index 55% rename from tests/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.test.ts rename to tests/Contexts/Mooc/CoursesCounter/application/Find/FindCourseCounterQueryHandler.test.ts index a52fd9a..260fa00 100644 --- a/tests/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.test.ts +++ b/tests/Contexts/Mooc/CoursesCounter/application/Find/FindCourseCounterQueryHandler.test.ts @@ -3,28 +3,37 @@ import { CoursesCounterMother } from '../../domain/CoursesCounterMother'; import { CoursesCounterRepositoryMock } from '../../__mocks__/CoursesCounterRepositoryMock'; import { CoursesCounterResponseMother } from '../../domain/CoursesCounterResponseMother'; import { CoursesCounterNotExist } from '../../../../../../src/Contexts/Mooc/CoursesCounter/domain/CoursesCounterNotExist'; +import { FindCoursesCounterQueryHandler } from '../../../../../../src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler'; +import { FindCoursesCounterQuery } from '../../../../../../src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery'; -describe('CoursesCounter Finder', () => { - let finder: CoursesCounterFinder; +describe('FindCourseCounter QueryHandler', () => { let repository: CoursesCounterRepositoryMock; beforeEach(() => { repository = new CoursesCounterRepositoryMock(); - finder = new CoursesCounterFinder(repository); }); + it('should find an existing courses counter', async () => { const counter = CoursesCounterMother.random(); - const expected = CoursesCounterResponseMother.create(counter.total); repository.returnOnSearch(counter); - const actual = await finder.run(); - + const handler = new FindCoursesCounterQueryHandler(new CoursesCounterFinder(repository)); + + const query = new FindCoursesCounterQuery(); + const response = await handler.handle(query); + repository.assertSearch(); - expect(expected).toEqual(actual); + + const expected = CoursesCounterResponseMother.create(counter.total); + expect(expected).toEqual(response); }); it('should throw an exception when courses counter does not exists', async () => { - await expect(finder.run()).rejects.toBeInstanceOf(CoursesCounterNotExist); + const handler = new FindCoursesCounterQueryHandler(new CoursesCounterFinder(repository)); + + const query = new FindCoursesCounterQuery(); + + await expect(handler.handle(query)).rejects.toBeInstanceOf(CoursesCounterNotExist); }); }); diff --git a/tests/Contexts/Mooc/CoursesCounter/domain/CoursesCounterResponseMother.ts b/tests/Contexts/Mooc/CoursesCounter/domain/CoursesCounterResponseMother.ts index d383200..677b388 100644 --- a/tests/Contexts/Mooc/CoursesCounter/domain/CoursesCounterResponseMother.ts +++ b/tests/Contexts/Mooc/CoursesCounter/domain/CoursesCounterResponseMother.ts @@ -1,8 +1,8 @@ import { CoursesCounterTotal } from '../../../../../src/Contexts/Mooc/CoursesCounter/domain/CoursesCounterTotal'; -import { CoursesCounterResponse } from '../../../../../src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterResponse'; +import { FindCoursesCounterResponse } from '../../../../../src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse'; export class CoursesCounterResponseMother { static create(total: CoursesCounterTotal) { - return new CoursesCounterResponse(total.value); + return new FindCoursesCounterResponse(total.value); } } From 80cb15f8a2806b421d962928f206d182208298be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Fri, 10 Jul 2020 17:52:43 +0200 Subject: [PATCH 11/12] Add lint step and activate eoline lint rule --- .github/workflows/nodejs.yml | 1 + package.json | 1 + tslint.json | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index c148dee..82a6447 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -25,6 +25,7 @@ jobs: run: | npm install npm run build --if-present + npm run lint npm test env: CI: true diff --git a/package.json b/package.json index 59a19f4..45a50f1 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test": "npm run test:unit && npm run test:features", "test:unit": "NODE_ENV=test jest", "test:features": "NODE_ENV=test cucumber-js -p default", + "lint": "tslint src/**/*.ts{,x}", "start": "NODE_ENV=production node dist/src/apps/mooc_backend/server", "build": "npm run build:clean && npm run build:tsc && npm run build:di", "build:tsc": "tsc -p tsconfig.prod.json", diff --git a/tslint.json b/tslint.json index b29bf01..7f72323 100644 --- a/tslint.json +++ b/tslint.json @@ -4,7 +4,7 @@ "rules": { "adjacent-overload-signatures": true, "curly": true, - "eofline": false, + "eofline": true, "align": [true, "parameters"], "class-name": true, "indent": [true, "spaces"], From 9c16582103d398592a43e08ea25afb5396d599d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Fri, 10 Jul 2020 17:56:54 +0200 Subject: [PATCH 12/12] Fix linter issues --- .../application/CreateCourseCommand.ts | 2 +- .../Find/FindCoursesCounterQuery.ts | 4 +-- .../Find/FindCoursesCounterQueryHandler.ts | 30 +++++++++---------- src/Contexts/Shared/domain/CommandBus.ts | 2 +- src/Contexts/Shared/domain/QueryBus.ts | 2 +- src/Contexts/Shared/domain/Response.ts | 2 +- .../QueryBus/QueryHandlersInformation.ts | 4 ++- tslint.json | 2 +- 8 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts b/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts index 199f777..884cf7d 100644 --- a/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts +++ b/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts @@ -17,4 +17,4 @@ export class CreateCourseCommand extends Command { this.name = name; this.duration = duration; } -} \ No newline at end of file +} diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts index 312dd47..bdbf087 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts @@ -1,3 +1,3 @@ -import { Query } from "../../../../Shared/domain/Query"; +import { Query } from '../../../../Shared/domain/Query'; -export class FindCoursesCounterQuery implements Query {} \ No newline at end of file +export class FindCoursesCounterQuery implements Query {} diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts index c8e85ab..b456d6f 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts @@ -1,17 +1,17 @@ -import { QueryHandler } from "../../../../Shared/domain/QueryHandler"; -import { FindCoursesCounterQuery } from "./FindCoursesCounterQuery"; -import { FindCoursesCounterResponse } from "./FindCoursesCounterResponse"; -import { Query } from "../../../../Shared/domain/Query"; -import { CoursesCounterFinder } from "./CoursesCounterFinder"; +import { QueryHandler } from '../../../../Shared/domain/QueryHandler'; +import { FindCoursesCounterQuery } from './FindCoursesCounterQuery'; +import { FindCoursesCounterResponse } from './FindCoursesCounterResponse'; +import { Query } from '../../../../Shared/domain/Query'; +import { CoursesCounterFinder } from './CoursesCounterFinder'; -export class FindCoursesCounterQueryHandler implements QueryHandler { - constructor(private finder: CoursesCounterFinder) {} +export class FindCoursesCounterQueryHandler + implements QueryHandler { + constructor(private finder: CoursesCounterFinder) {} - subscribedTo(): Query { - return FindCoursesCounterQuery; - } - handle(_query: FindCoursesCounterQuery): Promise { - return this.finder.run(); - } - -} \ No newline at end of file + subscribedTo(): Query { + return FindCoursesCounterQuery; + } + handle(_query: FindCoursesCounterQuery): Promise { + return this.finder.run(); + } +} diff --git a/src/Contexts/Shared/domain/CommandBus.ts b/src/Contexts/Shared/domain/CommandBus.ts index a4aebbc..ac32da3 100644 --- a/src/Contexts/Shared/domain/CommandBus.ts +++ b/src/Contexts/Shared/domain/CommandBus.ts @@ -2,4 +2,4 @@ import { Command } from './Command'; export interface CommandBus { dispatch(command: Command): Promise; -} \ No newline at end of file +} diff --git a/src/Contexts/Shared/domain/QueryBus.ts b/src/Contexts/Shared/domain/QueryBus.ts index 2a04f03..38c5b49 100644 --- a/src/Contexts/Shared/domain/QueryBus.ts +++ b/src/Contexts/Shared/domain/QueryBus.ts @@ -3,4 +3,4 @@ import { Response } from './Response'; export interface QueryBus { ask(query: Query): Promise; -} \ No newline at end of file +} diff --git a/src/Contexts/Shared/domain/Response.ts b/src/Contexts/Shared/domain/Response.ts index c1db157..158c809 100644 --- a/src/Contexts/Shared/domain/Response.ts +++ b/src/Contexts/Shared/domain/Response.ts @@ -1 +1 @@ -export interface Response {} \ No newline at end of file +export interface Response {} diff --git a/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts b/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts index e751eca..dd34e48 100644 --- a/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts +++ b/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts @@ -10,7 +10,9 @@ export class QueryHandlersInformation { this.queryHandlersMap = this.formatHandlers(queryHandlers); } - private formatHandlers(queryHandlers: Array>): Map> { + private formatHandlers( + queryHandlers: Array> + ): Map> { const handlersMap = new Map(); queryHandlers.forEach(queryHandler => { diff --git a/tslint.json b/tslint.json index 7f72323..b43960e 100644 --- a/tslint.json +++ b/tslint.json @@ -26,7 +26,7 @@ "no-bitwise": true, "no-debugger": true, "prefer-const": true, - "no-empty-interface": true, + "no-empty-interface": false, "no-string-throw": true, "unified-signatures": true, "space-before-function-paren": [