Issue with CQRS Implementation in NestJS with Mongoose

52 Views Asked by At

Context:

I'm currently working on a personal project to learn about the concepts of Command Query Responsibility Segregation (CQRS). The project involves a single object, Appointment, which is subject to typical CRUD operations.

I've explored existing implementations using NestJS and Mongoose, such as:

nestjs-rest-cqrs-example

ntestjs-ddd

Both examples use an object factory, but in the second example, it employs a factory on top of the existing schema. However, I find this extra layer verbose and seemingly unnecessary.

Problem:

In my implementation, I tried to avoid the extra factory layer and extended the schema directly from AggregateRoot. However, I encountered an error when performing a create operation:

TypeError: appointment.commit is not a function

The object is correctly created in the database, but the issue arises when trying to commit it in the publisher.

Code Snippets:

Appointment Schema:

@Schema({
  timestamps: true,
})
export class Appointment extends AggregateRoot {
  @Prop({ type: AppointmentInformation })
  info: AppointmentInformation;

  @Prop()
  start: string;

  @Prop()
  end: string;
}

export const AppointmentSchema = SchemaFactory.createForClass(Appointment);

export const AppointmentFeature = MongooseModule.forFeature([
  { name: Appointment.name, schema: AppointmentSchema },
]);

Controller:

@Post()
async createAppointment(@Body() payload: CreateAppointmentDto): Promise<void> {
  await this.commandBus.execute<CreateAppointmentCommand, Appointment>(
    new CreateAppointmentCommand(payload)
  );
}

Command handler:

@CommandHandler(CreateAppointmentCommand)
export class CreateAppointmentHandler implements ICommandHandler<CreateAppointmentCommand> {
  constructor(
    private readonly repository: AppointmentRepository,
    private readonly publisher: EventPublisher,
  ) {}

  async execute(command: CreateAppointmentCommand): Promise<Appointment> {
    const { payload } = command;

    const appointment = this.publisher.mergeObjectContext(
      await this.repository.createAppointment(payload as Appointment)
    );
    appointment.commit();
    return appointment;
  }
}

Repository:

export class AppointmentRepository extends BaseEntityRepository<Appointment> {
  constructor(
    @InjectModel(Appointment.name) private readonly appointmentModel: Model<Appointment>,
  ) {
    super(appointmentModel);
  }

  createAppointment(appointment: Appointment) {
    return this.create(appointment);
  }
}

Base repository:

export abstract class BaseEntityRepository<T extends AggregateRoot> {
  protected constructor(
    protected readonly entityModel: Model<T>
  ) {}

  protected create(entity: T): Promise<T> {
    return this.entityModel.create<T>(entity);
  }
}

Question

Am I missing something here? Do I really need to create a factory to act as another schema but extend from AggregateRoot? Any insights or suggestions on how to resolve the TypeError during the commit operation would be greatly appreciated.

0

There are 0 best solutions below