Q:

Mongoose multiple dependant queries

I am trying to save a new document to a collection and update two other documents in two different collections at the same time like this:
const newApplication = new ApplicationSchema({...})
newApplication.save()

EventSchema.findByIdAndUpdate(event_id, { $inc: { "application_counts": 1 } })

EventOwnerSchema.findByIdAndUpdate(event_owner_id, { $dec: { "balance": ticketPrice } })

These operations are related to each other and I want to make all of them fail if any of them fails. One possible solution maybe chaining them and reverting back previous updates, if in any step one of them fails.
const newApplication = new ApplicationSchema({...})
newApplication.save((err, res_1) => {
  if(err) {
    return false;
  }
  EventSchema.findByIdAndUpdate(event_id, { $inc: { "application_counts": 1 } }, (err, res_2) => {
    if(err) {
      ApplicationSchema.deleteOne({_id: res_1._id})
      return false;
    }
    EventOwnerSchema.findByIdAndUpdate(event_owner_id, { $dec: { "balance": ticketPrice } }, (err, res_3) => {
      if(err) {
        ApplicationSchema.deleteOne({_id: res_1._id})
        EventSchema.findByIdAndUpdate(event_id, { $dec: { "application_counts": 1 } })
        return false;
      }
      return true;
    })
  })
})

However, this solution doesn't look ideal for performance (and also for the same reason this problem exists but I am being too fussy I guess). Any suggestions?
Edit:
Footnote: I am not using embedded documents for other reasons.

A:

Transactions is the thing for this. Mongoose Transactions
It allows you to create a session, do some operation and abort/commit the changes you made inside the session.
const mongoose = require("mongoose");

// Start the session
const session = await mongoose.startSession();
session.startTransaction();

try {
    // Perform queries
    const newApplication = new ApplicationSchema({...})
    const p1 = newApplication .save();
    const p2 = EventSchema.findByIdAndUpdate(event_id, { $inc: { "application_counts": 1 } })
    const p3 = EventOwnerSchema.findByIdAndUpdate(event_owner_id, { $dec: { "balance": ticketPrice } })

    // Wait for the results
    const values = await Promise.all([p1, p2, p3]);

    // If did not throw any errors, commit changes and resolve
    await session.commitTransaction();
    resolve(values);
} catch (err) {
    // If an error occured, abort all changes and reject
    await session.abortTransaction();
    reject(err);
}

Thanks for the comment @Thomas Bormans

