I'm almost done with implementing event sourcing using CQRS module of NestJS, but now there is something I don't know how to handle. Currently, I have cron jobs that send transactions hourly to a my PUT /transactions endpoint. The payload is like this:
{
"status": "pending",
"amount": 10000,
"uid": "test123",
"transaction_id": "test-transaction1234",
}
When the transaction is sent to the endpoint, I don't know which fields have changed yet.
I have four posible commands: CreateTransactionCommand
, UpdateAmountCommand
, ApproveTransactionCommand
, DeclineTransactionCommand
. Two commands could occur at 'the same time', for example and UpdateAdmount and then an ApproveTransaction. The first one should aways occur at first.
How should I handle that? I was thinking about something like this, is it a good solution?
@Put()
async createOrUpdate(@Body() dto: Transaction) {
try {
await this.transactionsService.createTransaction(dto);
} catch (e) {
if (typeof e == ExistingTransactionError) console.error(e);
else throw e;
}
try {
await this.transactionsService.updateTransactionAmount(dto);
} catch (e) {}
if (dto.status === 'approved')
await this.transactionsService.approveTransaction(dto);
if (dto.status === 'declined')
await this.transactionsService.declineTransaction(dto);
}
Okay, I simply did a PUT endpoint, and thanks to the power of the aggregate root, it's pretty easy to know which command I can do.
@Put()
async createOrUpdate(@Body() dto: TransactionEventDto) {
try {
return await this.transactionsService.createTransaction({
...dto.payload,
status: 'pending',
});
} catch (e) {
if (!(e instanceof TransactionAlreadyCreatedError))
throw new HttpException(e.message, HttpStatus.BAD_REQUEST, e);
}
try {
const { transaction_id, amount, status } = dto.payload;
await this.transactionsService.updateTransactionAmount(
transaction_id,
amount,
);
if (status === 'approved') {
return await this.transactionsService.approveTransaction(
transaction_id,
);
}
if (status === 'declined') {
return await this.transactionsService.declineTransaction(
transaction_id,
);
}
return;
} catch (e) {
throw new HttpException(e.message, HttpStatus.BAD_REQUEST, e);
}
}
If the order matter, just use the 'sequential way' of promise (i.e await in chain)