GithubHelp home page GithubHelp logo

nestjs / cqrs Goto Github PK

View Code? Open in Web Editor NEW
691.0 20.0 133.0 4.88 MB

A lightweight CQRS module for Nest framework (node.js) :balloon:

Home Page: https://nestjs.com

License: MIT License

TypeScript 96.79% JavaScript 2.73% Shell 0.48%
nest nodejs typescript cqrs javascript nest-framework nest-cqrs

cqrs's Introduction

Nest Logo

A progressive Node.js framework for building efficient and scalable server-side applications.

NPM Version Package License NPM Downloads Travis Linux Coverage Discord Backers on Open Collective Sponsors on Open Collective

Description

A lightweight CQRS module for Nest framework (node.js)

Installation

$ npm install --save @nestjs/cqrs

Quick Start

Overview & CQRS Tutorial

Support

Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please read more here.

Stay in touch

License

Nest is MIT licensed.

cqrs's People

Contributors

brunnerlivio avatar caucik avatar dependabot[bot] avatar icetee avatar itka4yk avatar jbpionnier avatar kamilmysliwiec avatar kevintanjung avatar maciejsikorski avatar marsonya avatar micalevisk avatar notherdev avatar patryk-zielinski93 avatar renovate-bot avatar renovate[bot] avatar robertlowe avatar s-montigny-desautels avatar samgzman avatar sewas avatar sikora00 avatar spontoreau avatar tony133 avatar underfisk avatar vinceops avatar wodcz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cqrs's Issues

cqrs with rabbitmq

Is possible to integrate the cqrs module with rabbitmq or something like that?

Thanks for your help!

explorer-service does not find command handlers if command defined in index.ts

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

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.

Minimal reproduction of the problem with instructions

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

Environment


Nest version: 6.0.0

 
For Tooling issues:
- Node version: v8.9.4
- Platform:  Windows

Others:

Calling resolve(value) inside CommandHandler doesn't return value immediately

I'm submitting a...


[ ] Regression 
[x] Bug report?
[ ] Feature request
[x] Documentation issue or request

Current behavior

resolve(value) inside of a CommandHandler returns a value to the command caller after executing the Handler's body

Expected behavior

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.

Environment


Nest version: 5.4.0

ModuleRef error when registering command

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,
        ]);
    }
}

Support for transactions

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();
  }
}

No useful return type on QueryBus.execute

I'm submitting a...


[ ] 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.

Current behavior

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?

I am trying cqrs with nest-cqrs-example and when I try throw exception inside the command handler then the request get stuck.

I'm submitting a...


[ ] 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.

Current behavior

I am trying cqrs and nest-cqrs-example and when I try throw exception inside the command handler then the request get stuck.

My request - curl -X POST localhost:3000/hero/1/kill -d '{"dragonId": "abc"}' -H "Content-Type: application/json"

screenshot 2019-01-11 at 3 20 00 am

Source code

screenshot 2019-01-11 at 3 24 58 am

Error on console

screenshot 2019-01-11 at 3 24 08 am

Expected behavior

I think the request should get error immediately rather than deadlock if we didn't handle that exception.

Minimal reproduction of the problem with instructions

  1. clone nest-cqrs-example
  2. throw error inside the command handler.
  3. call corresponding endpoint.

What is the motivation / use case for changing the behavior?

N/A

Environment


  @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:

Event Handler / Saga / Where to Write Documentation

I'm submitting a...


[ ] 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:

  • Keep complicated async business logic at the saga level
  • Consolidate writes to a unified location (command handlers)

I 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:

  • writes -> command handlers
  • reads -> query handlers
  • more complex async logic / event sourcing-> saga

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!

Edit

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.

Request @EventsHandler and ofType support for inherited events

I'm submitting a...


[ ] 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.

Current behavior

@EventsHandler and ofType at the moment only handle the exact Event Class

Expected behavior

@EventsHandler and ofType work on classes it inherits from, or parent classes

Minimal reproduction of the problem with instructions


... 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

What is the motivation / use case for changing the behavior?

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.

Environment


Nest version: 5.3.7
CQRS version: 5.1.1

Can't inject dependencies in Sagas

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

Being able to use the injected dependencies in the saga methods.

Minimal reproduction of the problem with instructions

@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?

Saga: Command handler execution in synchronous way

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

Execute method is handle asynchronous way as should be, because always return a promise.

Environment


Nest version: 6.10.14

 
For Tooling issues:
- Node version: v12.13.1  

How to handle command errors

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?

Add comments to the public API

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

It would be great to see inline comments and prevent unnecessary type casting

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

Environment


Nest version: 7.1.2

 
For Tooling issues:
- Node version: 12.16.1  
- Platform: Linux 

Others:

Testing documentation missing

I'm submitting a...


[ ] 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.

Current behavior

As the issue title says there is no testing documentation

Expected behavior

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

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

Environment


Nest version: 7.1.2

 
For Tooling issues:
- Node version: 12.16.1  
- Platform: Linux 

Others:

EventBus access to underlying subject$

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

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!

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

Environment


Nest version: 6.10.1

 
For Tooling issues:
- Node version: 13.0.1
- Platform: Mac

Others:

Number of events being published grows with sub to EventBus in saga

I'm submitting a...


[x] Bug report

Current behavior

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.

Expected behavior

Only one event should be published at a time.

Minimal reproduction of the problem with instructions

@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) {}
}

What is the motivation / use case for changing the behavior?

This just seems like an unexpected behavior, but there's probably more going on that I anticipate.

Environment


Nest version: 6.0.4
CQRS Module version: 6.0.0

optionally await EventBus publish

I'm submitting a...


[ ] 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.

Current behavior

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.

Desired behavior

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.

Send events to Kafka / using event-sourcing pattern

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

Peer dependency

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

Naming inconsistency

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

AggregateRoot.commit() implementation breaks transactional semantics

I'm submitting a...


[ ] 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.

Current behavior

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:

commit() {
this[INTERNAL_EVENTS].forEach((event) => this.publish(event));
this[INTERNAL_EVENTS].length = 0;
}

Expected behavior

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.

What is the motivation / use case for changing the behavior?

Correct transactional handling of applied aggregate events

EventBus.publish() should return a Promise

I'm submitting a...

[x] Feature request

Current behavior

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.

Expected behavior

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.

Type 'null' / `undefined` is not assignable to type 'IQueryResult' when `strict: true`

I'm submitting a...


[ ] 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.

Current behavior

@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.

Expected behavior

Even if TypeScript compiler is in strict mode, null and undefined should be able to set as the return value of QueryBus#execute.

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

Environment


Nest core version: 7.0.0
Nest CQRS version: 6.1.0

 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

No Query bus?

I'm submitting a...


[ ] 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.

Expected behavior

Why does this library doesn't have a query bus? Is it a design choice or something else?

What is the motivation / use case for changing the behavior?

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.

Interceptors feature

I'm submitting a...


[ ] 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.

Current behavior

There is no implementation for interceptor in events, sagas, commands or queries

Expected behavior

Having interceptors would be useful very useful to perform metrics collection or any kind of operation before and after a command or query

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

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

Environment


Nest version: 7.1

 
For Tooling issues:
- Node version: 12  
- Platform:  Linux

Others:

Empty handlers when use with Testing Module

I'm submitting a...


[ ] 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.

Current behavior

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 {}

Expected behavior

When start application in develop mode

Result in CommandBus
handlers: Map { 'AddDeviceCommand' => AddDeviceHandler { publisher: [EventPublisher] } }

Minimal reproduction of the problem with instructions

Use https://github.com/kamilmysliwiec/nest-cqrs-example then create a TestingModule

What is the motivation / use case for changing the behavior?

Test Behavior for Command Handlers used in DeviceService

Environment


Nest version: 6.1.0
 
For Tooling issues:
- Node version: v10.15
- Platform:   Windows

Documentation for sagas and compensation for failures

I'm submitting a...


[ ] 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.

Expected behavior

It would be very useful for newcomers to the concept of Sagas to see an example with compensation for failures.

Ex:
OrderSaga

  • creates order
  • reserve inventory
  • reserve credit (It fails) so it reverts the order and inventory

'this' not available to event handlers in Types derived from AggregateRoot

Hello,

Thanks for this awesome ng-inspired library on 'express-js'. It's rocks (and well-designed, too)!!!!

This following line refers, please:

handler && handler(event);

CURRENT LOGIC

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.
}

SUPPOSED/CORRECT LOGIC

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?

Configurable EventBus in Sagas

I'm submitting a...


[ ] 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.

Current behavior

Saga can only use standard EventBus implementation

Expected behavior

It should be possible to inject custom implementation of EventBus into Saga (thus into CqrsModule)

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

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.

Environment


Nest version: 6.11.8

Extending to allow tracking of finished handlers and sagas

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

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.

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

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".

Environment


Nest version: latest

 
For Tooling issues:
- Node version: latest  
- Platform:  N/A 

Others:

CQRS does not handle concurrent requests safely

I'm submitting a...


[ ] 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.

Current behavior

Concurrent requests are currently not be handled safely.

Expected behavior

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.

Minimal reproduction of the problem with instructions

Trigger an event from two threads that will be handled in parallel.

What is the motivation / use case for changing the behavior?

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.

Environment


Nest version: 5.0.0

How to use 2 separate databases, one for the write side and one for the read side. (two databases synchronized via eventbus)

I'm submitting a...


[ ] 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.

Current behavior

Expected behavior

how to use 2 separate databases, one for the write side and one for the read side. (two databases synchronized via eventbus)

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

Environment


Nest version: 6.0.0

 
For Tooling issues:
- Node version: v10.15.1 
- Platform:  Windows  

Others:

Error while using inside TestingModule (Empty Handlers)

It is wrong behavior and initialization of CQRS inside test module which is used for my unit tests.

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.

Current behavior

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!

Expected behavior

I expect that the code of FooHandler execute method will be invoked and a new console record will be printed.

What is the motivation case for changing the behavior?

I also tried to use NestFactory.createApplicationContext instead of Test.createTestingModule and I received a passed test.

So there two possible issues:

  • Something in CQRS module can not be properly set up for testing modules. I did not found any possible places in sources of @nestjs/cqrs.
  • This is a bug in the testing module

Environment


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

Suggestion for IEventPublisher interface

I'm submitting a...


[ ] 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.

Current behavior

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
    }
}

Expected behavior

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
  }
}

Minimal reproduction of the problem with instructions

I'd love to provide one if that supports discussion but actually it comes down to the code snippets mentioned above.

What is the motivation / use case for changing the behavior?

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?).

Environment


Nest version: 6.10.1

 
For Tooling issues:
- Node version: 13.0.1
- Platform:  Mac

Others:

Saga: "ofType" has two different behaviors, the wrong one is exposed

I'm submitting a...


[ ] 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.

Current behavior

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).

Expected behavior

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).

Environment


Nest version: 5.4+
CQRS version: 5.1.1

 
For Tooling issues:
- Node version: 10.15+
- Platform:  Debian 9+

Others:
TypeScript 3.2+

Workaround

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).

CQRS module lack of support for hybrid microservice

Bug Report

Current behavior

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'));`

Environment


Nest version: 6.0.0
 
For Tooling issues:
- Node version: 10.14.1
- Platform:  Mac

Others:
using VSCode

EventBus and @EventHandler decorator

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

If I add a @EventsHandler decorator, the class handle my event whatever his name.

Minimal reproduction of the problem with instructions

Two event handlers with the same name for the same event but in different module.

What is the motivation / use case for changing the behavior?

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.

Environment


Nest version: 6.1.1
Nest CQRS version: 6.0.0

 
For Tooling issues:
- Node version: v10.6.0  
- Platform:  Mac 


Event handler not working in the unit test?

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)
image

This is my TenantCreated handler:
image

This is how I do the test initialization:
image

The test itself:
image

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?

Rebuild npm package

I'm submitting a...


[ ] 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.

Current behavior

@kamilmysliwiec Github code does not match the NPM package for version 5.1.1. Could the NPM package be rebuilt?

Change getEventName access modifier in AggregateRoot from private to protected

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

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 :)

Command handler dependency injection scope

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

Command handlers should be using dependency injection container when executing commands. And not when command handlers are being registered.

Minimal reproduction of the problem with instructions

@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
  }
}

What is the motivation / use case for changing the behavior?

Environment


Nest version: 6.0.4

 
For Tooling issues:
- Node version: v10.15.0  
- Platform: Linux 

Others:

Saga: Responding to event by issuing multiple commands

I'm submitting a...


[ ] 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.

Current behavior

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.

Expected behavior

A saga can return multiple commands in response to an event.

Minimal reproduction of the problem with instructions

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()));
            }),
        );
    }
}

What is the motivation / use case for changing the behavior?

Having a saga that is able to issue multiple commands in response to an event.

Environment


Nest version: 5.4+
CQRS version: 5.1.1

 
For Tooling issues:
- Node version: 10.11.0
- Platform:  Mac

Others:

Bump version up to 7.x

I'm submitting a...


[ ] 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.

Current behavior

npm warns about unmet dependencies during installation:

npm WARN @nestjs/[email protected] requires a peer of @nestjs/[email protected]^6.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @nestjs/[email protected] requires a peer of @nestjs/[email protected]^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.

Expected behavior

New version of core nest module, should pull a trigger to update whole bunch of related dependencies to update their requirements

Minimal reproduction of the problem with instructions

    "@nestjs/common": "^7.0.5",
    "@nestjs/core": "^7.0.5",
    "@nestjs/cqrs": "^6.1.0",

What is the motivation / use case for changing the behavior?

BC I LOVE NEST <3

Environment


Nest version: 7.0.5

 
For Tooling issues:
- Node version: v12.13.1  
- Platform: Linux  

Others:

Configurable Event Bus support

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?

more complex project sample

I'm submitting a...

[ ] Regression
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request

Current behavior

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?

Expected behavior

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

What is the motivation / use case for changing the behavior?

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.