-
Notifications
You must be signed in to change notification settings - Fork 222
Part four query bus #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a2874ee
762b037
71a896f
3c9c97f
784ab21
2543df4
3ad53bc
a4605d2
a69946d
69f44d6
d6b7729
80cb15f
9c16582
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ jobs: | |
run: | | ||
npm install | ||
npm run build --if-present | ||
npm run lint | ||
npm test | ||
env: | ||
CI: true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<CreateCourseCommand> { | ||
constructor(private courseCreator: CourseCreator) {} | ||
|
||
subscribedTo(): Command { | ||
return CreateCourseCommand; | ||
} | ||
|
||
async handle(command: CreateCourseCommand): Promise<void> { | ||
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 }); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { Query } from '../../../../Shared/domain/Query'; | ||
|
||
export class FindCoursesCounterQuery implements Query {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<FindCoursesCounterQuery, FindCoursesCounterResponse> { | ||
constructor(private finder: CoursesCounterFinder) {} | ||
|
||
subscribedTo(): Query { | ||
return FindCoursesCounterQuery; | ||
} | ||
handle(_query: FindCoursesCounterQuery): Promise<FindCoursesCounterResponse> { | ||
return this.finder.run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export abstract class Command {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { Command } from './Command'; | ||
|
||
export interface CommandBus { | ||
dispatch(command: Command): Promise<void>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { Command } from './Command'; | ||
|
||
export interface CommandHandler<T extends Command> { | ||
subscribedTo(): Command; | ||
handle(command: T): Promise<void>; | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export abstract class Query {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { Query } from './Query'; | ||
import { Response } from './Response'; | ||
|
||
export interface QueryBus { | ||
ask<R extends Response>(query: Query): Promise<R>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Query } from './Query'; | ||
import { Response } from './Response'; | ||
|
||
export interface QueryHandler<Q extends Query, R extends Response> { | ||
subscribedTo(): Query; | ||
handle(query: Q): Promise<R>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export interface Response {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { Command } from '../../domain/Command'; | ||
import { CommandHandler } from '../../domain/CommandHandler'; | ||
import { CommandNotRegisteredError } from '../../domain/CommandNotRegisteredError'; | ||
|
||
export class CommandHandlersInformation { | ||
private commandHandlersMap: Map<Command, CommandHandler<Command>>; | ||
|
||
constructor(commandHandlers: Array<CommandHandler<Command>>) { | ||
this.commandHandlersMap = this.formatHandlers(commandHandlers); | ||
} | ||
|
||
private formatHandlers(commandHandlers: Array<CommandHandler<Command>>): Map<Command, CommandHandler<Command>> { | ||
const handlersMap = new Map(); | ||
|
||
commandHandlers.forEach(commandHandler => { | ||
handlersMap.set(commandHandler.subscribedTo(), commandHandler); | ||
}); | ||
|
||
return handlersMap; | ||
} | ||
|
||
public search(command: Command): CommandHandler<Command> { | ||
const commandHandler = this.commandHandlersMap.get(command.constructor); | ||
|
||
if (!commandHandler) { | ||
throw new CommandNotRegisteredError(command); | ||
} | ||
|
||
return commandHandler; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Command } from '../../domain/Command'; | ||
import { CommandBus } from './../../domain/CommandBus'; | ||
import { CommandHandlersInformation } from './CommandHandlersInformation'; | ||
|
||
export class InMemoryCommandBus implements CommandBus { | ||
constructor(private commandHandlersInformation: CommandHandlersInformation) {} | ||
|
||
async dispatch(command: Command): Promise<void> { | ||
const handler = this.commandHandlersInformation.search(command); | ||
|
||
await handler.handle(command); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<R extends Response>(query: Query): Promise<R> { | ||
const handler = this.queryHandlersInformation.search(query); | ||
|
||
return handler.handle(query) as Promise<R>; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
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<Query, QueryHandler<Query, Response>>; | ||
|
||
constructor(queryHandlers: Array<QueryHandler<Query, Response>>) { | ||
this.queryHandlersMap = this.formatHandlers(queryHandlers); | ||
} | ||
|
||
private formatHandlers( | ||
queryHandlers: Array<QueryHandler<Query, Response>> | ||
): Map<Query, QueryHandler<Query, Response>> { | ||
const handlersMap = new Map(); | ||
|
||
queryHandlers.forEach(queryHandler => { | ||
handlersMap.set(queryHandler.subscribedTo(), queryHandler); | ||
}); | ||
|
||
return handlersMap; | ||
} | ||
|
||
public search(query: Query): QueryHandler<Query, Response> { | ||
const queryHandler = this.queryHandlersMap.get(query.constructor); | ||
|
||
if (!queryHandler) { | ||
throw new QueryNotRegisteredError(query); | ||
} | ||
Comment on lines
+28
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we handling these exception somewhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This exception and the CommandNotRegisteredError are handled by the controller, throwing a 500 error. Is not handled as a specific error but its a general error. |
||
|
||
return queryHandler; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😍😊