Comments (5)
@moltar I ended up making my own wrapper around ICommandHandler, where I made a base class for Queries and a base class for Mutations.
The query version only fetches data, so that does not require a transaction or concurrency checks.
For the mutation version I added the @transaction decorator for TypeORM in combination with their versioning column on my aggregate root.
from cqrs.
BaseCommandHandler is the wrapper. I notice it ended up removing the @transaction decorator, not sure why to be honest. Since typescript does not have the concept of unit of work like C# does, I ended up (mis)using the entityManager from TypeORM for it. Combined with a version column ( @VersionColumn()) on my quiz model, the transaction fails if concurrent requests are handled.
I hope it helps :)
import { ICommandHandler, EventPublisher } from "@nestjs/cqrs";
import { InjectEntityManager } from "@nestjs/typeorm";
import { EntityManager } from "typeorm";
export abstract class BaseCommandHandler<T> implements ICommandHandler<T> {
async execute(command: T, resolve: (value?) => any): Promise<any> {
try {
const result = await this.onExecute(command);
resolve(result);
} catch(e) {
console.error('Command Handler error: ', e);
resolve(e);
}
}
abstract async onExecute(command: T): Promise<any>;
}
import { CommandHandler, EventPublisher } from '@nestjs/cqrs';
import { Quiz } from '../../models/quiz.model';
import { InjectEntityManager } from '@nestjs/typeorm';
import { EntityManager } from 'typeorm';
import { QuizStateEnum } from '../../enums/quiz-state.enum';
import { BaseCommandHandler } from '../../../common/commands/base.command-handler';
import { StopQuizCommand } from '../stop-quiz.command';
@CommandHandler(StopQuizCommand)
export class StopQuizCommandHandler extends BaseCommandHandler<StopQuizCommand> {
constructor(
private readonly _publisher: EventPublisher,
@InjectEntityManager()
private readonly _entityManager: EntityManager,
) {
super();
}
/**
* @param {StopQuizCommand} command
* @returns {Promise<any>}
*/
async onExecute(command: StopQuizCommand): Promise<any> {
const quiz = this._publisher.mergeObjectContext(
await this._entityManager.findOneOrFail(Quiz, +command.id)
);
// Set quiz state to GameOver
// Possibly, in the future, cleanup or sync data
await quiz.changeState(QuizStateEnum.GameOver, this._entityManager);
quiz.currentQuestion = null;
await this._entityManager.save(quiz);
quiz.commit();
return true;
}
}
import { ICommandHandler, CommandHandler, EventPublisher } from '@nestjs/cqrs';
import { Quiz } from '../../models/quiz.model';
import { InjectEntityManager } from '@nestjs/typeorm';
import { EntityManager, MoreThan } from 'typeorm';
import { QuizStateEnum } from '../../enums/quiz-state.enum';
import { Question } from '../../models/question.model';
import { BaseCommandHandler } from '../../../common/commands/base.command-handler';
import { StartNextQuestionCommand } from '../start-next-question.command';
import { QuizHasNoMoreQuestionsEvent } from '../../events/quiz-has-no-more-questions.event';
@CommandHandler(StartNextQuestionCommand)
export class StartNextQuestionCommandHandler extends BaseCommandHandler<StartNextQuestionCommand> {
constructor(
private readonly _publisher: EventPublisher,
@InjectEntityManager()
private readonly _entityManager: EntityManager,
) {
super();
}
/**
* @param {StartNextQuestionCommand} command
* @returns {Promise<any>}
*/
async onExecute(command: StartNextQuestionCommand): Promise<any> {
const quiz = this._publisher.mergeObjectContext(
await this._entityManager.findOneOrFail(Quiz, +command.quizId)
);
await this._entityManager.transaction(async manager => {
// Get next question or game over
const nextQuestions = await this._entityManager.find(Question, {
where: {
id: MoreThan(+quiz.currentQuestion),
quizId: +command.quizId
},
order: { id: "ASC" },
take: 1
});
// If there is no next question, emit event (which sets quiz to GameOVer)
if (!nextQuestions.length) {
quiz.apply(new QuizHasNoMoreQuestionsEvent({
quizId: quiz.id
}));
return;
}
// Set next question and update state to question feedback
await quiz.changeState(QuizStateEnum.QuestionPreview, manager);
quiz.currentQuestion = nextQuestions[0].id;
await manager.save(quiz);
});
quiz.commit();
return true;
}
}
from cqrs.
@timvandesteeg what motivated you to close this issue? Do you feel this is addressed or did you find another solution?
from cqrs.
@timvandesteeg , and maybe you will share your wrapper?
from cqrs.
@timvandesteeg would you also be kind to share an example of how typeorm entity works together with nestjs/crqrs/AggregateRoot
? :)
from cqrs.
Related Issues (20)
- TypeError: You provided 'undefined' where a stream was expected HOT 2
- Middleware chain for command and querybud HOT 2
- Saga events - request context HOT 1
- documentation request HOT 1
- problems constructing instance with Mikro ORM HOT 1
- Feat: Register Commands, Queries & Events programatically HOT 1
- Multiple execution of events based on how many times module is imported HOT 1
- Adjust EventsHandler typing to accept the type of class extending IEvent
- Double events when using microservice HOT 2
- Sagas stop working in case of exceptions HOT 2
- Command handler becomes unresponsive HOT 4
- Event Handler fails to Trigger After Error Occurs HOT 1
- Use a decorator to automatically merge event publisher into AggregateRoot HOT 1
- Http Exceptions thrown in certain situations cause an app crash. HOT 1
- App crashes if event handler triggers a command that throws HOT 1
- CommandHandleException with Command Name not command uuid HOT 2
- Commit method throws an error when trying to publish domain events HOT 1
- Patch update reflect-metadata HOT 1
- Distributed Systems: Sagas & Location Transparency HOT 1
- Cannot read properties of undefined (reading 'id') - possible breaking change for 8.0.1 HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cqrs.