import {Repository} from '@orion-js/services'
import {createCollection, typedId} from '@orion-js/mongodb'
import {schemaWithName, InferSchemaType} from '@orion-js/schema'
const AccountSchema = schemaWithName('AccountSchema', {
_id: {type: typedId('account')},
balance: {type: Number},
owner: {type: String}
})
const TransactionSchema = schemaWithName('TransactionSchema', {
_id: {type: typedId('trx')},
fromId: {type: String},
toId: {type: String},
amount: {type: Number},
date: {type: Date}
})
type AccountType = InferSchemaType<typeof AccountSchema>
type TransactionType = InferSchemaType<typeof TransactionSchema>
@Repository()
export class AccountRepository {
accounts = createCollection({
name: 'accounts',
schema: AccountSchema
})
transactions = createCollection({
name: 'transactions',
schema: TransactionSchema
})
async transferFunds(fromId: string, toId: string, amount: number) {
// Get the MongoDB client from the collection
const client = this.accounts.client.client;
// Start a session
const session = client.startSession();
try {
// Start a transaction
session.startTransaction();
// Deduct from source account
const sourceUpdateResult = await this.accounts.updateOne(
{_id: fromId, balance: {$gte: amount}},
{$inc: {balance: -amount}},
{mongoOptions: {session}} // Pass the session to the operation
);
if (sourceUpdateResult.modifiedCount === 0) {
// Abort the transaction if no document was updated
await session.abortTransaction();
throw new Error('Insufficient funds');
}
// Add to destination account
await this.accounts.updateOne(
{_id: toId},
{$inc: {balance: amount}},
{mongoOptions: {session}} // Pass the session to the operation
);
// Record the transaction
await this.transactions.insertOne(
{
fromId,
toId,
amount,
date: new Date()
},
{mongoOptions: {session}} // Pass the session to the operation
);
// Commit the transaction
await session.commitTransaction();
return true;
} catch (error) {
// Abort the transaction on error
await session.abortTransaction();
throw error;
} finally {
// End the session
await session.endSession();
}
}
// Alternative implementation using withTransaction
async transferFundsWithTransaction(fromId: string, toId: string, amount: number) {
const client = this.accounts.client.client;
const session = client.startSession();
try {
// Use the withTransaction helper from the MongoDB driver
return await session.withTransaction(async () => {
// Deduct from source account
const sourceUpdateResult = await this.accounts.updateOne(
{_id: fromId, balance: {$gte: amount}},
{$inc: {balance: -amount}},
{mongoOptions: {session}}
);
if (sourceUpdateResult.modifiedCount === 0) {
throw new Error('Insufficient funds');
}
// Add to destination account
await this.accounts.updateOne(
{_id: toId},
{$inc: {balance: amount}},
{mongoOptions: {session}}
);
// Record the transaction
await this.transactions.insertOne(
{
fromId,
toId,
amount,
date: new Date()
},
{mongoOptions: {session}}
);
return true;
});
} finally {
await session.endSession();
}
}
}