Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Asynchronous Configuration in CqrsModule #1912

Open
1 task done
masterj3y opened this issue Feb 3, 2025 · 4 comments
Open
1 task done

Add Support for Asynchronous Configuration in CqrsModule #1912

masterj3y opened this issue Feb 3, 2025 · 4 comments
Labels

Comments

@masterj3y
Copy link

Is there an existing issue that is already proposing this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe it

Currently, the CqrsModule only provides a forRoot method for configuration, which requires static options at the time of module initialization. However, in many applications, configuration values (such as feature flags, database connections, or external API calls) need to be resolved asynchronously, making it difficult to dynamically configure the CQRS module.

Describe the solution you'd like

Proposed Solution:
Introduce a forRootAsync method in CqrsModule, allowing asynchronous configuration using useFactory, useClass, or useExisting. This would enable dependency injection and support dynamic configurations.

Suggested API:

@Module({
  imports: [
    CqrsModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        enableSagas: configService.get<boolean>('ENABLE_SAGAS', true),
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

Teachability, documentation, adoption, migration strategy

No response

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

Benefits:

  • Enables fetching configurations dynamically (e.g., from environment variables or APIs).
  • Supports dependency injection for enhanced flexibility.
  • Aligns with NestJS best practices for module configuration.

This feature would be especially useful for applications utilizing microservices, distributed event handling, or feature flag-based CQRS logic.

@kamilmysliwiec
Copy link
Member

Image

I don't think any of these should be provided asynchronously. Am I missing something?

@masterj3y
Copy link
Author

Unable to provide RabbitMQEventPublisher asynchronously in the CqrsModule

I have created a custom RabbitMQEventPublisher for event publishing in my NestJS application, and I need to provide it asynchronously so that I can inject AmqpConnection from the @golevelup/nestjs-rabbitmq package.

I am using a wrapper module to configure the RabbitMQEventPublisher as the event publisher for CqrsModule. However, I am encountering issues with this approach.

Current Code Setup:

import { AmqpConnection, RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
import { DynamicModule, ModuleMetadata } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { CqrsModule, CqrsModuleOptions } from '@nestjs/cqrs';
import { RabbitMQEventPublisher } from 'src/event-publishers/rabbitmq.event-publisher';


export class CqrsWrapperModule {
  static async forRootAsync(options: CqrsWrapperModuleAsyncOptions): Promise<DynamicModule> {
    return {
      module: CqrsWrapperModule,
      global: true,
      providers: [
        {
          provide: 'CQRS_WRAPPER_OPTIONS',
          useFactory: options.useFactory,
          inject: options.inject || [],
        },
        {
          provide: RabbitMQEventPublisher,
          useFactory: (configService: ConfigService, amqpConnection: AmqpConnection) => new RabbitMQEventPublisher(configService, amqpConnection),
          inject: [ConfigService, AmqpConnection],
        },
      ],
      imports: [
        RabbitMQModule.forRootAsync({
          useFactory: (configService: ConfigService) => ({
            uri: configService.get<string>('RMQ_URI')!,
            heartbeat: configService.get<string>('RMQ_HEARTBEAT')!,
            exchanges: [{ name: configService.get<string>('RMQ_EXCHANGE_NAME')!, type: 'topic' }],
            registerHandlers: true,
            durable: true,
            exclusive: false,
            prefetchCount: 1,
            enableControllerDiscovery: true,
            connectionInitOptions: { wait: false },
          }),
          inject: [ConfigService],
        }),
        {
          module: CqrsModule,
          providers: [
            {
              provide: 'CQRS_MODULE_OPTIONS',
              useFactory: async (options: CqrsWrapperModuleOptions, rabbitMQEventPublisher: RabbitMQEventPublisher): Promise<CqrsModuleOptions> => ({
                eventPublisher: rabbitMQEventPublisher,
              }),
              inject: ['CQRS_WRAPPER_OPTIONS', RabbitMQEventPublisher],
            },
          ],
          exports: ['CQRS_MODULE_OPTIONS'],
        },
      ],
      exports: ['CQRS_WRAPPER_OPTIONS', CqrsModule, RabbitMQEventPublisher],
    };
  }
}

@kamilmysliwiec
Copy link
Member

Sounds good. Would you like to create a PR for this? You could use this module as a reference https://github.com/nestjs/axios/blob/master/lib/http.module.ts

@masterj3y
Copy link
Author

Thank you for the suggestion! I’ll go ahead and create a PR. I’ll refer to the module you mentioned for structure and implementation. I’ll make sure to follow the existing patterns and conventions used in the codebase.

Let me know if there’s anything else you’d like me to consider.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants