nestjs / cqrs Goto Github PK
View Code? Open in Web Editor NEWA lightweight CQRS module for Nest framework (node.js) :balloon:
Home Page: https://nestjs.com
License: MIT License
A lightweight CQRS module for Nest framework (node.js) :balloon:
Home Page: https://nestjs.com
License: MIT License
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
The current implementation of AggregateRoot.prototype.commit()
invokes this.publish()
for each applied event one-by-one iteratively. Unfortunately, this prevents publishing all events as a unit transactionally, which IMHO defeats the purpose of a commit:
Lines 22 to 25 in 9e4e21a
In practice, AggreateRoot.publish()
currently forwards to IEventBus.publish()
following a call to either EventPublisher.prototype.mergeObjectContext
or EventPublisher.prototype.mergeClassContext
. Instead of iteratively calling publish(), these APIs should use a publishAll
API instead. Surprisingly, publishAll()
already exists on the IEventBus
interface but does not appear to be used anywhere.
Correct transactional handling of applied aggregate events
[ ] Regression
[ ] Bug report
[ ] Feature request
[X] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Whenever I try to respond to an event within a saga by issuing multiple commands an error gets thrown and my application crashes.
Debugging showed me that the error seems to be 'CommandHandler not found exception!', but I don't know where this error arises from - when returning just 1 command instead of multiple ones then everything is working fine.
A saga can return multiple commands in response to an event.
My saga simplified looks like this:
@Injectable()
export class SomeSagas {
public constructor() {}
onSomeEvent(events$: EventObservable<any>): Observable<ICommand> {
return events$.ofType(SomeEvent).pipe(
map((event: SomeEvent) => {
return of(new SomeCommand(uuid()), new SomeCommand(uuid()));
}),
);
}
}
Having a saga that is able to issue multiple commands in response to an event.
Nest version: 5.4+
CQRS version: 5.1.1
For Tooling issues:
- Node version: 10.11.0
- Platform: Mac
Others:
[ ] Regression
[ ] Bug report
[ ] Feature request
[X ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
As the issue title says there is no testing documentation
Experienced nest developers know and can manage to get it but it would be great to have some docs for new users or even to guide the existing ones
Nest version: 7.1.2
For Tooling issues:
- Node version: 12.16.1
- Platform: Linux
Others:
Is possible to integrate the cqrs module with rabbitmq or something like that?
Thanks for your help!
I came across really strange issue.
I described it in Discord but I think I haven't miss anything (but still probably have...) and it looks like some strange issue with Nest or maybe just docs?
I have command which is creating the Tenant. It is emiting the event TenantCreated. In memberships, event handler listening to this event and creating admin role for user which created the tenant.
I had it async but figure out this one should be exception and I should wait until MembershipCreated for this Tenant id and user id is emmited so as soon as user will get the response, he will be for sure assigned to the tenant as admin.
This is my command handler (some parts omitted)
This is my TenantCreated handler:
This is how I do the test initialization:
and the issue is that it hangs... During this unit test, TenantCreated event never reaches the handler so command gets stuck...
An interesting fact is that during E2E test with a full-blown app it is working like a charm without any issues...
I would be really thankful if someone would point the issue...
The event handler is visible in the providers so it should be registered (it gets created!) and receive the events (it does not), so I hope this is not my fault?
[ ] Regression
[ ] Bug report
[x] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
It's relatively easy to figure from the docs and looking up the related sources that in order to track event dispatching, one needs to implement a custom publisher.
However, the actual event handlers and saga streams look very opaque... I don't know if there is a way to extend tracking of their execution.
If there isn't a way to track executing, the base classes should be modified to expose the properties that would allow for such tracking to be implemented in extended classes, even if the user can't do this tracking without an extended class.
If there is such a way, it should be documented, along with extending the publisher.
I would be willing to write such an extension guide myself (as an extra recipe?) after I actually make one that can do this sort of tracking.
I would like to create a custom event storage mechanism, whereby I track when event handlers were dispatched, when each event handler finished executing, and when all registered sagas related to an event have finished executing.
All of this in an effort to make my application resiliant against application restarts - on restart, the still not safe for deletion events would be re-fed to the application, unfinished handlers would be re-executed (my application will have as a matter of policy that each handler should use only one transaction during its execution, ensuring such re-execution would not result in unexpected results...), and unfinished sagas would wait for the remaining events to appear and execute their respective commands, before the events are marked for deletion.
On a related note, I also have a question that the docs don't make clear... Do sagas even wait for the handlers of an event to finish executing, or is the mere act of dispatching an event enough to trigger all sagas that track it? From reading the sources, it seems like the answer to that is "No, sagas don't wait for event handlers to finish. Sagas are executed as soon as all tracked events are dispatched".
Nest version: latest
For Tooling issues:
- Node version: latest
- Platform: N/A
Others:
[ ] Regression
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
My main concern is understanding a good place where to do writes
.
I'm enjoying setting up the CQRS module in my current project, but I ended up dropping event handlers because I couldn't justify a real good use for them.
From command
handlers I am publishing events with the eventBus
, then creating sagas
for events and additional command handlers for those because I want to:
saga
levelI chose this approach so that I can onboard others into the project and if they have questions about where to do writes/reads/more complex async logic, I can direct them as such:
Am I missing something real obvious about event handlers? I fully understand I can inject into them and I can handle write logic there, but is there something better about the pattern that I am not groking?
Thanks!
I realize after reading/watching more about CQRS that this is probably just me needing to understand the pattern better.
For most of my use cases, especially for single transactions that don't require communication with other aggregates, I see that an Event Handler
is a good place to do work. Transitioning to a Saga
seems to come into play when there are multiple transactions that need to occur, but even then at the end of the Saga
I'm not sure what I would need to do in the Command Handler
at the end of a Saga
if I'm executing transactions inside of the Saga
.
[ ] Regression
[ ] Bug report
[X] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Cool that NestJS provides a way to use the CQRS pattern!! ๐
Just a little thing here: when providing a custom event publisher (by implementing IEventPublisher
and IMessageSource
) one needs to connect it with the existing eventbus. However, the eventbus does not give access to the underlying subject$
defined in the ObservableBus
. That results in sth. like
this.myEventPublisher.bridgeEventsTo((this.eventBus as any).subject$);
I'd love to get rid of this (this.eventBus as any).subject$
and use this.eventBus.subject$
directly.
I think the used eventbus should provide access to the underlying subject$
.
So how about changing the access modifier in ObservableBus
from protected
to public
or provide a getter?
If there are no concerns with that solution, I'd love to open a PR!
Nest version: 6.10.1
For Tooling issues:
- Node version: 13.0.1
- Platform: Mac
Others:
[ ] Regression
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Why does this library doesn't have a query bus? Is it a design choice or something else?
The same way command buses are useful for separating the user intention from the execution logic, querybus would be useful for separating the query from its execution logic.
Getting the following error when I try to register my LoginCommandHandler. When I comment it out, it works just fine.
...\node_modules\@nestjs\core\injector\module-ref.js:24
throw new unknown_element_exception_1.UnknownElementException();
^
Error: Nest cannot find given element (it does not exist in current context)
at Object.findInstanceByPrototypeOrToken (...\node_modules\@nestjs\core\injector\module-ref.js:24:19)
at Object.get (...\node_modules\@nestjs\core\injector\module.js:259:29)
at CommandBus.registerHandler (...\node_modules\@nestjs\cqrs\dist\command-bus.js:44:41)
at handlers.forEach.handler (...\node_modules\@nestjs\cqrs\dist\command-bus.js:38:42)
at Array.forEach (<anonymous>)
at CommandBus.register (...\node_modules\@nestjs\cqrs\dist\command-bus.js:38:18)
at AuthorizationModule.onModuleInit (...\src\modules\authorization\authorization.module.ts:63:25)
at NestApplication.callModuleInitHook (...\node_modules\@nestjs\core\nest-application-context.js:65:39)
Minimal example:
@Module({
imports: [
CQRSModule
],
providers: [
LoginCommand,
LoginCommandHandler <-- forgot this line
],
})
export class AuthorizationModule implements OnModuleInit {
/**
* @param {ModuleRef} _moduleRef
* @param {CommandBus} _commands$
*/
constructor(
private readonly _moduleRef: ModuleRef,
private readonly _commands$: CommandBus
) {}
onModuleInit() {
this._commands$.setModuleRef(this._moduleRef);
this._commands$.register([
LoginCommandHandler,
]);
}
}
[ ] Regression
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
npm warns about unmet dependencies during installation:
npm WARN @nestjs/[email protected] requires a peer of @nestjs/common@^6.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @nestjs/[email protected] requires a peer of @nestjs/core@^6.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @nestjs/[email protected] requires a peer of [email protected] but none is installed. You must install peer dependencies yourself.
New version of core nest module, should pull a trigger to update whole bunch of related dependencies to update their requirements
"@nestjs/common": "^7.0.5",
"@nestjs/core": "^7.0.5",
"@nestjs/cqrs": "^6.1.0",
BC I LOVE NEST <3
Nest version: 7.0.5
For Tooling issues:
- Node version: v12.13.1
- Platform: Linux
Others:
[ ] Regression
[x] Bug report?
[ ] Feature request
[x] Documentation issue or request
resolve(value) inside of a CommandHandler returns a value to the command caller after executing the Handler's body
As stated in the recipe under the CommandsHandler example code, the resolve(value) function should return a value to the caller and then continue executing where it left off. It is not clear if this is a bug or just misguiding documentation.
Nest version: 5.4.0
in my projects, i want to allow event to change datas, how can do this with cqrs?
[ ] Regression
[ ] Bug report
[ ] Feature request
[ x ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Concurrent requests are currently not be handled safely.
Parallel requests should be run in an transaction and/or possibly use a versioning protection (e.g. for TypeORM: http://typeorm.io/#/decorator-reference/versioncolumn) in the aggregate root. This way, your application would be protected from concurrent requests.
If two concurrent requests would arrive, the first one should be handled before validating the second request.
Trigger an event from two threads that will be handled in parallel.
Commands and events live in an asynchronous world, where concurrent requests could harm the state of the system. The current, minimal setup for CQRS in NestJS is very promising, but not yet usable for production.
Nest version: 5.0.0
Specifically interested in wiring up the handlers after creating the TestModule
[ ] Regression
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
It would be very useful for newcomers to the concept of Sagas to see an example with compensation for failures.
Ex:
OrderSaga
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Using cqrs sample app provided here if I move the commands definitions into index.ts
file along with the commandHandlers array the explorer-service does not find the associated command handlers.
As long as my command handler is decorater with the correct attribute explore-service should be able to discover it regardless of where the commands are defined.
Clone my fork of the sample here and look at this file https://github.com/aguerot/nest-cqrs-example/blob/master/src/heroes/commands/index.ts
. If you run the sample and invoke kill dragon command you get an exception:
[Nest] 7296 - 2019-03-19 18:20 [ExceptionsHandler] CommandHandler not found exception! +6653ms
Nest version: 6.0.0
For Tooling issues:
- Node version: v8.9.4
- Platform: Windows
Others:
[ ] Regression
[ ] Bug report
[ ] Feature request
[x ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
@kamilmysliwiec Github code does not match the NPM package for version 5.1.1. Could the NPM package be rebuilt?
[ ] Regression
[ ] Bug report
[X] Feature request
[] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
When trying to inject dependencies into the saga's constructor, they get registered on init.
But when accessing this dependency in the Saga method they seem to be undefined.
Being able to use the injected dependencies in the saga methods.
@Injectable()
export class UserSagas {
public constructor(@Inject(ContactRepository) private readonly contactRepository: ContactRepository) { }
@Saga()
onAddedUserEvent(events$: Observable<any>): Observable<ICommand[]> {
return events$.pipe(
ofType(AddedUserEvent),
map(async (event: AddedUserEvent): Promise<ICommand[]> => {
const contacts = await this.contactRepository.getUserContacts(event.userId);
return contacts.map(contact => new NotifyContactCommand(contact, event.userId))
}),
flatMap(c => c)
);
}
}
this
seems to be undefined. Is there a way to inject dependencies in saga's for when you need additional information to trigger new commands?
[ ] Regression
[ ] Bug report
[ ] Feature request
[x] Refactoring (added because it's not a bug nor a feature)
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Currently the event name resolution in the AggregateRoot in private. It's a limitation in the system, especially when your events are stored in an external store. For exemple with Event Store your events will be stored in JSON format with a "type" property to resolve them. In this kind of case, when you're loading the history the different objects doesn't match with any classes of your project (they are just plain object without any type). In consequences, the apply method can not call the "on" handler methods inside the AggregateRoot.
The method getEventName can be protected so we can override it to change the name resolution of the event. It's not really a bug or a feature (it's just a refactoring to add extensibility to the class).
Example for an aggregate that load events from Event Store:
protected getEventName(event): string {
if(event.type) {
return event.type;
} else {
// Default behavior (not really usefull for this example
// but can be use for events that aren't handled by a
// publisher dedicated to EventStore)
return super.getEventName(event);
}
}
PR is comming!
Thanks :)
[x] Bug report
When subscribed to the EventBus
in a flattening operator (mergeMap
) in a @Saga
, an AggregateRoot
is publishing all old events as well as a new one for each subsequent Command
being executed.
If I do a take(1)
off of the EventBus
this stop occurring.
Only one event should be published at a time.
@CommandHandler(DoThing)
export class DoThingHandler implements ICommandHandler<DoThing> {
constructor(private someData: SomeDataAccessService) {}
async execute(command: DoThing) {
const model = this.someData.connectToWriteModel();
await model.create(command.dto);
model.commit();
}
}
import { SomeWriteModel } from './some-write.model';
@Injectable()
export class SomeDataAccessService {
constructor(
@InjectModel('SomeModel')
private readonly someModel: Model<SomeDocument>,
private readonly publisher: EventPublisher,
) {}
connectToWriteModel() {
return this.publisher.mergeObjectContext(
new SomeWriteModel(this.someModel),
);
}
}
export class SomeWriteModel extends AggregateRoot {
constructor(
private readonly doc: Model<CompanyDocument>,
) {
super();
}
async create(dto: SomeDto) {
let someDoc: SomeDocument;
try {
someDoc = await this.doc.findOne({ name: dto.name });
if (someDoc) {
// on every subsequent DoThing command
// this block is only run once
// but the event itself keeps adding +1
// first time through: 1 SomeDocCreated, second time 2 will be published at the same time, then 3...
this.apply(
new SomeDocCreated(someDoc.name, someDoc._id.toString()),
);
return;
}
}
//.. etc
}
}
// SomeSaga
@Injectable()
export class SomeFeatureSagas {
@Saga()
someBigSaga$ = (
events$: Observable<SomeBigSagaKickedOff>,
): Observable<DoMoreThings> =>
events$.pipe(
ofType(SomeBigSagaKickedOff),
tap(s => this.commandBus.execute(new DoSomethingElse(s.foo))),
mergeMap(s =>
this.eventBus.pipe(
filter<SomeDocCreated>(
c =>
c instanceof SomeDocCreated &&
s.foo === c.foo,
),
// take(1), <---- with a take(1), the behavior stops, without this many events are published
map(
c =>
new DoMoreThings(),
),
),
),
);
constructor(readonly commandBus: CommandBus, readonly eventBus: EventBus) {}
}
This just seems like an unexpected behavior, but there's probably more going on that I anticipate.
Nest version: 6.0.4
CQRS Module version: 6.0.0
[ ] Regression
[ ] Bug report
[X] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
I have an abstract custom IEvent
implementation that provides information like aggregate version and an unique event id:
export abstract class AbstractEvent implements IEvent {
public sourceId: string;
public aggregateVersion: number;
private readonly _eventId: string;
constructor() { this._eventId = uuid(); }
public get eventId() { return this._eventId; }
}
Every event in my application extends this class.
If one implements an event store by implementing the IEventPublisher
, there is no good way to access the AbstractEvent
properties, due to the current implementation here. At the moment an implementation looks like this:
export class CustomEventStore implements IEventPublisher {
public publish<T extends IEvent>(event: T) {
// Acces to event of type IEvent. In order o get event id, for example, a cast is necessary
}
}
How about changing the interface to a generic one like
export interface IEventPublisher<T extends IEvent = IEvent> {
publish(event: T);
}
An implementation could then look like that:
export class CustomEventStore implements IEventPublisher<AbstractEvent> {
publish(event: AbstractEvent) {
// Publish logic here with ability to use props like aggregate version, event id and so on
}
}
I'd love to provide one if that supports discussion but actually it comes down to the code snippets mentioned above.
I want every single event in an application to contain at least two properties: an unique event id and a version property. The latter one refers to the version of the aggregate. The version is incremented whenever the aggregate receives a new event. This is achieved by all events extending the AbstractEvent
. When storing my events in the event store, I want to have access to some of this information for the sake of event-id comparison and/or checking for concurrent write access (Maybe that can be part of nestjs/CQRS, as well?).
Nest version: 6.10.1
For Tooling issues:
- Node version: 13.0.1
- Platform: Mac
Others:
[ ] Regression
[ ] Bug report
[ ] Feature request
[x ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
how to use 2 separate databases, one for the write side and one for the read side. (two databases synchronized via eventbus)
Nest version: 6.0.0
For Tooling issues:
- Node version: v10.15.1
- Platform: Windows
Others:
CQRS exposes 3 decorators and 3 interfaces. All of them are singular form, except one:
CommandHandler
ICommandHandler
QueryHandler
IQueryHandler
EventsHandler
IEventHandler
I think it should be EventHandler
Maybe you can change this during next breaking change release
[ ] Regression
[ ] Bug report
[X] Feature request
[X] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
eventBus.publish(new Event())
does not return anything, but it would be a nice feature if it could act like commandBus.execute()
and allow you to await asynchronous operations within a REST route.
Common complaints I've heard/read about the CQRS system is that it's a lot of boilerplate for apps that don't necessarily need it. For apps that do, I'll say that it seems like an amazing feature! Making use of events directly, though, is a likely first stage for moving bits of an application in this direction. Additionally, when developing independent modules, it can be a helpful approach to emit events so that more specific business implementations can simply hook in to those events to perform follow on tasks. For example, a UserModule
may have basic features for handling users without the specifics of the business needs. In order to add additional features to the application, typically, additional modules can simply depend on UserModule
and use alternate endpoints for more specific business actions. This works in most cases, but there are scenarios where a developer would want to do something on createUser
, but before the user creation route returns. Making use of a hooks-like pattern for this solves the isolated module problem by allowing another module(like TaskModule
) to perform an action when the user is created (like attaching a default task to the user), but before the REST response returns. This may be a poor example, but I'd be happy to elaborate with more.
While it may not be possible with your event architecture, it would be a nice feature if async event handlers could be awaited from eventBus.publish
, or if a workaround could be established without the overhead of the full CQRS featureset.
Thanks in advance. I'm just a developer trying to write clean code. Feel free to tell me I'm wrong if I'm way off base.
[ ] Regression
[x ] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Command handler dependencies are being injected on module init. But since NestJS v6 there are provider scopes. And currently it's not possible to get request object in command handler.
Command handlers should be using dependency injection container when executing commands. And not when command handlers are being registered.
@CommandHandler(Test)
export class TestHandler implements ICommandHandler<Test> {
@Inject(REQUEST)
private request!: Request;
public async execute(command: Test): Promise<any> {
console.log(this.request); // undefined
}
}
Nest version: 6.0.4
For Tooling issues:
- Node version: v10.15.0
- Platform: Linux
Others:
I was wondering how the team envisioned a configurable event bus that can delegate to an external pub/sub and queueing mechanism like AMQP, Amazon SQS/SNS, Google Cloud Pub/Sub (I believe it's queued internally).
I was planning to just extend the event bus and override certain methods. However, I believe if we can instead allow injection of a publisher/subscriber in the event bus, it would allow users to do as they please on the event handling.
export class EventBus extends ObservableBus<IEvent> implements IEventBus {
publish<T extends IEvent>(event: T) {
if(this.eventPublisher){
...publish to external queue...
} else {
this.subject$.next(event);
}
}
bind<T extends IEvent>(handler: IEventHandler<IEvent>, name: string) {
const stream$ = name ? this.ofEventName(name) : this.subject$;
stream$.subscribe(event => handler.handle(event));
if(this.eventSubscriber){
...subscribe a worker to the external queue...
...once a message is consumed call "this.subject$.next(event);"...
}
}
}
Instead of the clunky if-else blocks, we can have a "DefaultPublisher" and "DefaultSubscriber" that immediately consumes a published messaged and thus, immediately triggering this.subject$.next(event)
similar to how it is right now.
Any thoughts?
Is there way to make Command Bus and Queue bus external by using Kafka or RabbitMQ under the hood? Thanks.
[ ] Regression
[X] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
When I use the CqrsModule in a TestingModule, the CommandBus does not get the handles
const module: TestingModule = await Test.createTestingModule({
imports: [
CqrsModule,
DatabaseModule,
TypegooseModule.forFeature(Device),
ProductModule,
],
providers: [AddDeviceHandler, DeviceService],
controllers: [DeviceController],
}).compile();
Result in CommandBus handlers: Map {}
When start application in develop mode
Result in CommandBus
handlers: Map { 'AddDeviceCommand' => AddDeviceHandler { publisher: [EventPublisher] } }
Use https://github.com/kamilmysliwiec/nest-cqrs-example then create a TestingModule
Test Behavior for Command Handlers used in DeviceService
Nest version: 6.1.0
For Tooling issues:
- Node version: v10.15
- Platform: Windows
Hello,
It's more like a question than issue, but I could not find any info about this: does CQRS module (or core Nest.js) supports transactions with TypeORM? I didn't see any examples with transactions and I'm not sure if it is possible to make any further queries in EventHandler
under the same transaction as in the command's execute
method if any was started.
Secondary: does command run in transaction automatically or is it even possible to do it this way? In examples I saw an example with PhotoService
, would it be possible to run it under transaction like:
@Component()
export class PhotoService {
constructor(
@Inject('PhotoRepositoryToken') private readonly photoRepository: Repository<Photo>) {}
@Transaction()
async findAll(): Promise<Photo[]> {
// normally using you need to get the EntityManager as a parameter and request a repository from it
// and `this.photoRepository` cannot be used as it's outside of transaction scope
return await this.photoRepository.find();
}
}
I have already found and written comment inside same problem
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
My current target is to verify the handler code by calling related command via command bus.
This is a simple example of my command and handler:
export class FooCommand {}
@CommandHandler(FooCommand)
export class FooHandler implements ICommandHandler<FooCommand> {
constructor() {}
async execute(command: FooCommand): Promise<void> {
console.log('It works');
return;
}
}
Then I try to inspect it and create a unit test:
describe('foo handler', () => {
let fooModule;
before(async () => {
fooModule = await Test.createTestingModule({
imports: [
CqrsModule,
],
controllers: [],
providers: [
FooHandler
]
}).compile();
});
it('it should works', async () => {
const commandBus: CommandBus = fooModule.select(CqrsModule).get(CommandBus);
await commandBus.execute(new FooCommand());
});
});
Now test fails with error: Error: The command handler for the "AddAttacksCommand" command was not found!
I expect that the code of FooHandler
execute method will be invoked and a new console record will be printed.
I also tried to use NestFactory.createApplicationContext
instead of Test.createTestingModule
and I received a passed test.
So there two possible issues:
Nest version:
@nestjs/core: 7.0.1
@nestjs/testing: 7.0.11
@nestjs/common: 7.0.1
@nestjs/cqrs: 7.0.0
For Tooling issues:
- Node version: v12.14.1
- Platform: Mac
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
I am trying cqrs and nest-cqrs-example and when I try throw exception inside the command handler then the request get stuck.
curl -X POST localhost:3000/hero/1/kill -d '{"dragonId": "abc"}' -H "Content-Type: application/json"
I think the request should get error immediately rather than deadlock if we didn't handle that exception.
N/A
@nestjs/common: `5.5.0`
@nestjs/core: `5.5.0`
@nestjs/cqrs: `5.1.1`
For Tooling issues:
- Node version: `10.12.0`
- Platform: Mac
Others:
[ ] Regression
[ ] Bug report
[ x ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
@EventsHandler and ofType at the moment only handle the exact Event Class
@EventsHandler and ofType work on classes it inherits from, or parent classes
... export class EventBase implements IEvent { ...
... export class EventOne extends EventBase { ...
..
@EventsHandler(EventBase)
export class EventBaseHandler implements IEventHandler {
...
model.emitRequest(new EventOne());
model.commit();
...
Notes : EventBaseHandler will not be called because it does not check for inheriting class
Assuming there is a need to enforce 1 event = 1 event-handler, at least ofType
(or alternative function) from saga be given option to support parent class since saga is suppose to support 1..* events.
Nest version: 5.3.7
CQRS version: 5.1.1
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
@Injectable()
export class MyService {
constructor(private queryBus: QueryBus) {}
async getBook(bookId: string): Promise<Book | null> {
return this.queryBus.execute<GetBookQuery, Book | null>(new GetBookQuery(bookId))
}
}
error TS2344: Type 'Book | null' does not satisfy the constraint 'IQueryResult'.
Type 'null' is not assignable to type 'IQueryResult'
This error happens not only with null
but also undefined
.
Even if TypeScript compiler is in strict mode, null
and undefined
should be able to set as the return value of QueryBus#execute
.
Nest core version: 7.0.0
Nest CQRS version: 6.1.0
For Tooling issues:
- Node version: XX
- Platform:
Others:
Hi!
In our team we are planning to use event-sourcing pattern with kafka. We really likes nest and it's CQRS module. We were wondering if there is a way to automatically launch the events to kafka.
We can really implement it by ourselves, but we would like to ask if any of you use it with nest. It would be very nice to have some shared experiences :)
I guess that things like sagas may not be possible to implement with kafka. In any case, did you have any experience with event-sourcing and kafka? :D
How command handlers should implement exception/error handling? Now if execute method throws an error I get node error "UnhandledPromiseRejectionWarning: Unhandled promise rejection".
Should implement catch all exceptions and use resolve function to return something like CommandResult object which have information was command success or not?
[x] Feature request
At the moment, the publish
method of the EventBus
does return void
. When publishing an event, how can I make sure that all handlers have been executed? Without this, I'm at risk of having lost some messages.
EventBus
should return Promise<void>
. void
because we should not expect any return value.
Others
Do you have any objection against this? The promise would resolve when all handlers have been called.
[ ] Regression
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request
Currently the package provides a sample project which is fine but it doesn't provides some basic use cases e.g. creating a new hero Entity.
This might be simple but let's assume you would want to use a database (e.g. with TypeORM) and would come up with a database Entity
@Entity()
export class Hero extends BaseEntity {
@PrimaryGeneratedColumn('uuid')
public id: string;
}
and your aggregate
export class Hero extends AggregateRoot {
constructor(private readonly id: string) {
super();
}
}
To me the docs are lacking some information about some practises. The aggregate only comes up in the events part. Do you have to create two models? One for the database and one for the aggregate?
It would be nice to have a more complex sample project. Maybe a CRUD project (e.g. users or tasks) with a simple database integration.
Even more awesome would be a sample project using Event Sourcing. Currently Event Sourcing only comes up in the summary
Currently there are many articles about the CQRS theory out there. But there are almost no samples on how to implement such architectures. The only project I found was this one
https://github.com/qas/examples-nodejs-cqrs-es-swagger
but if I understood correctly even this one is not a "real world project". It creates a new user in the repository by creating a new instance of the aggregate but it does not persist it somewhere.
[ ] Regression
[ ] Bug report
[x ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
When dispatching a query from the QueryBus, the return type of the execute
method is IQueryResult
. If the intent of a query is to return a response, it would be useful to have type inference for the expected response. I would also imagine that a return type for commands would be equally useful. Has this been considered? Or am I misinterpreting the proper way to implement this pattern?
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
A saga is a function taking an EventObservable
as a parameter, and returning an Observable<ICommand>
.
In the documentation, there is an example using events$.ofType(HeroKilledDragonEvent)
where events$
is the EventObservable
.
ofType
is supposed to accept a list of IEvent
(rest param), according to its definition. However, the saga is executed with the EventBus
has a parameter, which also has ofType
but accepting (and working with) only one IEvent
as a parameter (see EventBus
#register
).
I'd expect to be able to list multiple IEvent
in the implementation of my saga, using ofType
. The typedef is valid, but the implem is not (the executed ofType
is the one of EventBus
, not ObservableBus
).
Nest version: 5.4+
CQRS version: 5.1.1
For Tooling issues:
- Node version: 10.15+
- Platform: Debian 9+
Others:
TypeScript 3.2+
For anyone encountering this issue (before I find the time to submit a pull request ๐ ), what I personally did is declaring my parameter as an EventBus
(what it is) and doing the type-filtering myself (as EventBus
extends Observable
, we can benefit from all operators of RxJs).
As per the Transactional outbox Pattern the database update and sending of the message must be atomic in order to avoid data inconsistencies and bugs.
There might be a case where event bus might be down or database transaction (local one) fails. right?
Does this module tackle and solve this problem by using the OUTBOX table and by mining the Transaction Log?
Hello,
I'm going through the tutorial on your website and I'm trying to run "npm install" after i cloned thenest-typescript-starter.
Just wanted to let you know of the below warnings and unmet peer dependency.
โโโฌ UNMET PEER DEPENDENCY [email protected]
npm WARN @nestjs/[email protected] requires a peer of [email protected] but none was installed.
npm WARN @nestjs/[email protected] requires a peer of [email protected] but none was installed.
npm WARN @nestjs/[email protected] requires a peer of [email protected] but none was installed.
Thanks
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Triggered command handler is execute in synchronous way, but ICommandHandler
interface define that execute
method should return promise. As result of that, program never wait until promise is resolved.
Execute method is handle asynchronous way as should be, because always return a promise.
Nest version: 6.10.14
For Tooling issues:
- Node version: v12.13.1
Hello,
I'm building a microservice that will support both TCP and RMQ connections,
If I use the regular microservice set up for my project the CQRS works great.
like so:
` const microserviceOptions = {
transport: Transport.RMQ,
options: {
queue: 'data',
urls: ['amqp://localhost:5672'],
},
}
const app = await NestFactory.createMicroservice(AppModule, microserviceOptions);
app.listen(() => console.log("Microservice is listening"));`
but if change it to support hybrid application I get the
CommandHandler not found exception!
` const microserviceOptions = {
transport: Transport.RMQ,
options: {
queue: 'data',
urls: ['amqp://localhost:5672'],
},
};
const app = await NestFactory.create(AppModule);
app.connectMicroservice(microserviceOptions);
app.startAllMicroservices(() => console.log('Microservice is listening'));`
Nest version: 6.0.0
For Tooling issues:
- Node version: 10.14.1
- Platform: Mac
Others:
using VSCode
Hi,
I created this issue on stackoverflow a while ago but didn't get much help.
Any idea here?
[ ] Regression
[X ] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
I have two event handler class with the same name for the same event but with different logic and in different module. When my bus receive the event concerned, only last event handler registered handle these. If I change the class name of one of EventHandler it's works.
For information, Explorer Service (events) has no problem to get these event handlers.
If I add a @EventsHandler decorator, the class handle my event whatever his name.
Two event handlers with the same name for the same event but in different module.
I have multiple bounded context (one per module) and it's more easy for me if the event handler class is the name of event with EventHandler suffix.
If I add bounded context name on prefix, its works, but the name of class is sooooooo long.
Nest version: 6.1.1
Nest CQRS version: 6.0.0
For Tooling issues:
- Node version: v10.6.0
- Platform: Mac
[ ] Regression
[ ] Bug report
[X ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
There is no implementation for interceptor in events, sagas, commands or queries
Having interceptors would be useful very useful to perform metrics collection or any kind of operation before and after a command or query
I'm trying to build a command visualizer to know the ongoing operations and having a command history to analyze in production. I think more people would find useful having a tracking way on operations done in CQRS, the module is really good and this would be an enhancement
Nest version: 7.1
For Tooling issues:
- Node version: 12
- Platform: Linux
Others:
[ ] Regression
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Currently CqrsModule connects sagas with EventBus in onModuleInit
method, accepting CommandBus and EventBus as constructor parameters. It means consuming module cannot replace EventBus implmentation.
Saga can only use standard EventBus
implementation
It should be possible to inject custom implementation of EventBus
into Saga (thus into CqrsModule)
I need to handle errors thrown from command handlers. For that, I need to change how command are executed inside EventBus
implementation (standard implementation simply calls commandBus.execute()
, without awaiting and any way to handle errors, I want to change it). But when I implemented my own event bus, I cannot provide it to CqrsModule, to be used when connecting sagas.
Nest version: 6.11.8
[ ] Regression
[ ] Bug report
[ ] Feature request
[X ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Currently, the exported CQRS module functionalities do not provide comments which would help to jump into website documentation or checking examples as the other nest modules do.
Another issue is that QueryResult does not display the correct interface, either the one that the command returns or the one you provide, it requires always a cast.
It would be great to see inline comments and prevent unnecessary type casting
Nest version: 7.1.2
For Tooling issues:
- Node version: 12.16.1
- Platform: Linux
Others:
Hello,
Thanks for this awesome ng-inspired library on 'express-js'. It's rocks (and well-designed, too)!!!!
This following line refers, please:
Line 33 in 78635ed
With the current logic
handler && handler(event);
The 'this' context-variable represents the onEventTypeName()
function INSTEAD of the AggregateRoot
(or its derivative) instance.
. . .
name: string
. . .
onEventTypeName(event: EventTypeName) {
// 'this' here represents the function
this.name = event.name; // it breaks, doesn't work.
}
But, with the following logic
handler && handler.call(this, event);
The 'this' context-variable represents the AggregateRoot
(or its derivative) instance, as desired.
. . .
name: string;
. . .
onEventTypeName(event: EventTypeName) {
// 'this' here represents the AggregateRoot (or its derivate), i.e. the parent of this function.
// Thus, is can access field/other members of the class
this.name = event.name; // works
}
If the supposed logic is right, do you mind a pull request or you prefer to just fixed it up from your end?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.