Skip to content

Commit

Permalink
Feature | Group question answer - cmd handler and repo (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaszdworniczak authored Apr 24, 2021
1 parent 51f12da commit 1869295
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 6 deletions.
8 changes: 7 additions & 1 deletion backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { MongoQuestionsRepository } from './modules/questions/infrastructure/rep
import { InMemoryQuestionsRepository } from './modules/questions/infrastructure/repository/inmemory/InMemoryQuestionsRepository';
import { QuestionsRestApiModule } from './modules/questions/presentation/rest-api/QuestionsRestApiModule';
import { InMemoryGroupQuestionsRepository } from './modules/questions/infrastructure/repository/inmemory/InMemoryGroupQuestionsRepository';
import { InMemoryAnswerGroupQuestionRepository } from './modules/group-question-answer/infrastructure/repository/inmemory/InMemoryAnswerGroupQuestionRepository';

config();

Expand All @@ -50,8 +51,9 @@ export async function IntegramicApplication(
await connectToMongoDb();
}

const answerGroupQuestionRepository = AnswerGroupQuestionRepository();
const groupQuestionAnswerModule: Module = {
core: AnswerGroupQuestionModuleCore(eventBus, currentTimeProvider),
core: AnswerGroupQuestionModuleCore(eventBus, currentTimeProvider, answerGroupQuestionRepository),
restApi: GroupQuestionAnswerRestApiModule(commandBus, eventBus, queryBus),
};

Expand Down Expand Up @@ -152,3 +154,7 @@ function QuestionsRepository() {
function GroupQuestionsRepository() {
return new InMemoryGroupQuestionsRepository();
}

function AnswerGroupQuestionRepository() {
return new InMemoryAnswerGroupQuestionRepository();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import { AnswerGroupQuestionCommandHandler } from './application/command/AnswerG
import { AnswerGroupQuestion } from './application/command/AnswerGroupQuestion';
import { DomainEventPublisher } from '../../../shared/core/application/event/DomainEventBus';
import { CurrentTimeProvider } from '../../../shared/core/CurrentTimeProvider';
import { AnswerGroupQuestionRepository } from './application/AnswerGroupQuestionRepository';

export function AnswerGroupQuestionModuleCore(eventPublisher: DomainEventPublisher, currentTimeProvider: CurrentTimeProvider): ModuleCore {
export function AnswerGroupQuestionModuleCore(
eventPublisher: DomainEventPublisher,
currentTimeProvider: CurrentTimeProvider,
repository: AnswerGroupQuestionRepository,
): ModuleCore {
return {
commandHandlers: [
{
commandType: AnswerGroupQuestion,
handler: new AnswerGroupQuestionCommandHandler(eventPublisher, currentTimeProvider),
handler: new AnswerGroupQuestionCommandHandler(eventPublisher, currentTimeProvider, repository),
},
],
eventHandlers: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { QuestionAnswer } from './domain/QuestionAnswer';

export interface AnswerGroupQuestionRepository {
save(answeredGroupQuestion: QuestionAnswer): Promise<void>;

findByGroupId(groupId: string): Promise<QuestionAnswer | undefined>;

findAllByQuestionId(questionId: string): Promise<QuestionAnswer[]>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@ import { AnswerGroupQuestion } from './AnswerGroupQuestion';
import { CommandResult } from '../../../../../shared/core/application/command/CommandResult';
import { DomainEventPublisher } from '../../../../../shared/core/application/event/DomainEventBus';
import { CurrentTimeProvider } from '../../../../../shared/core/CurrentTimeProvider';
import { PlayerId } from '../../../../../shared/core/domain/PlayerId';
import { AnswerGroupQuestionRepository } from '../AnswerGroupQuestionRepository';
import { answerGroupQuestion } from '../domain/QuestionAnswer';

export class AnswerGroupQuestionCommandHandler implements CommandHandler<AnswerGroupQuestion> {
constructor(private readonly eventPublisher: DomainEventPublisher, private readonly currentTimeProvider: CurrentTimeProvider) {}
constructor(
private readonly eventPublisher: DomainEventPublisher,
private readonly currentTimeProvider: CurrentTimeProvider,
private readonly repository: AnswerGroupQuestionRepository,
) {}

async execute(command: AnswerGroupQuestion): Promise<CommandResult> {
const { state, events } = answerGroupQuestion(command, this.currentTimeProvider);

await this.repository.save(state);
this.eventPublisher.publishAll(events);
return CommandResult.success();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { CurrentTimeProvider } from '../../../../../shared/core/CurrentTimeProvider';
import { DomainCommandResult } from '../../../../../shared/core/domain/DomainCommandResult';
import { GroupQuestionWasAnswered } from './event/GroupQuestionWasAnswered';

export class QuestionAnswer {
readonly questionId: string;
readonly groupId: string;
readonly answerAuthorId: string;
readonly text: string;

constructor(props: { questionId: string; groupId: string; answerAuthorId: string; text: string }) {
this.questionId = props.questionId;
this.groupId = props.groupId;
this.answerAuthorId = props.answerAuthorId;
this.text = props.text;
}
}

export function answerGroupQuestion(
command: { questionId: string; groupId: string; answerAuthorId: string; text: string },
currentTimeProvider: CurrentTimeProvider,
): DomainCommandResult<QuestionAnswer> {
if (command.text.trim().length <= 0) {
throw new Error('An answer cannot be empty!');
}

const groupQuestionWasAnswered = new GroupQuestionWasAnswered({
occurredAt: currentTimeProvider(),
...command,
});

const questionAnswer = new QuestionAnswer({ ...command });

return {
state: questionAnswer,
events: [groupQuestionWasAnswered],
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export class GroupQuestionWasAnswered {
readonly occurredAt: Date;
readonly questionId: string;
readonly groupId: string;
readonly answerAuthorId: string;
readonly text: string;

constructor(props: { occurredAt: Date; questionId: string; groupId: string; answerAuthorId: string; text: string }) {
this.questionId = props.questionId;
this.groupId = props.groupId;
this.answerAuthorId = props.answerAuthorId;
this.text = props.text;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { AnswerGroupQuestionRepository } from '../../../core/application/AnswerGroupQuestionRepository';
import { QuestionAnswer } from '../../../core/application/domain/QuestionAnswer';

export class InMemoryAnswerGroupQuestionRepository implements AnswerGroupQuestionRepository {
private readonly entities: { [id: string]: QuestionAnswer } = {};

findByGroupId(groupId: string): Promise<QuestionAnswer | undefined> {
return Promise.resolve(this.entities[groupId]);
}

async save(question: QuestionAnswer): Promise<void> {
this.entities[question.questionId] = question;
}

findAllByQuestionId(questionId: string): Promise<QuestionAnswer[]> {
return Promise.resolve(
Object.keys(this.entities)
.filter((id) => this.entities[id].groupId === questionId)
.map((id) => this.entities[id]),
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { CommandPublisher } from '../../../../shared/core/application/command/CommandBus';
import { DomainEventPublisher } from '../../../../shared/core/application/event/DomainEventBus';
import { QueryPublisherMock } from '../../../../../test/test-support/shared/core/QueryPublisherMock';
import { QueryPublisher } from '../../../../shared/core/application/query/QueryBus';
import express, { Request, Response } from 'express';
import { AnswerGroupQuestion } from '../../core/application/command/AnswerGroupQuestion';
Expand Down

0 comments on commit 1869295

Please sign in to comment.