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