Banking App (MongoDB)

This document outlines the MongoDB database schema for a sample banking application. The schema is designed to be efficient, scalable, and easy to understand. We use Mongoose for defining our data models.

The core of our application revolves around four main collections: User, Account, Transaction and Ledger.

Installation Guide

To add these schemas to your project, run:

npx servercn-cli add schema banking-app

1. User Schema

The User schema stores essential information about the customer. This includes personal details and security credentials.

MVC Path: src/models/user.model.ts

Feature Path: src/modules/auth/user.model.ts

import mongoose, { Document, Model, Schema } from "mongoose";
 
export interface IAvatar {
  public_id: string;
  url: string;
  size: number;
}
 
export interface IUser extends Document {
  _id: mongoose.Types.ObjectId;
  name: string;
  email: string;
  password?: string;
  role: "user" | "admin";
  isEmailVerified: boolean;
  lastLoginAt?: Date;
  failedLoginAttempts: number;
  lockUntil?: Date;
  avatar?: IAvatar;
 
  provider: "local" | "google" | "github";
  providerId?: string;
 
  isDeleted: boolean;
  deletedAt?: Date | null;
  reActivateAvailableAt?: Date | null;
 
  createdAt: Date;
  updatedAt: Date;
}
 
const userSchema = new Schema<IUser>(
  {
    name: {
      type: String,
      required: [true, "Name is required"],
      trim: true
    },
    email: {
      type: String,
      required: [true, "Email is required"],
      unique: true,
      lowercase: true,
      trim: true
    },
    password: {
      type: String,
      select: false,
      default: null
    },
    provider: {
      type: String,
      enum: ["local", "google", "github"],
      default: "local"
    },
    providerId: {
      type: String,
      default: null
    },
    role: {
      type: String,
      enum: ["user", "admin"],
      default: "user"
    },
    avatar: {
      public_id: String,
      url: String,
      size: Number
    },
    isEmailVerified: {
      type: Boolean,
      default: false
    },
    lastLoginAt: {
      type: Date
    },
    failedLoginAttempts: {
      type: Number,
      required: true,
      default: 0
    },
    lockUntil: {
      type: Date
    },
    isDeleted: {
      type: Boolean,
      default: false
    },
    deletedAt: {
      type: Date,
      default: null
    },
    reActivateAvailableAt: {
      type: Date,
      default: null
    }
  },
  {
    timestamps: true
  }
);
 
// Performance Indexes
userSchema.index({ provider: 1, providerId: 1 }); // Quick lookup for OAuth
userSchema.index({ role: 1 });
userSchema.index({ isDeleted: 1 }); // Optimized for soft-delete queries
 
const User: Model<IUser> =
  mongoose.models.User || mongoose.model<IUser>("User", userSchema);
 
export default User;

Installation

npx servercn-cli add schema banking-app/user

2. Account Schema

The Account schema stores user bank accounts (e.g., savings, current). A user can own multiple accounts with different types, currencies, and statuses.

MVC Path: src/models/account.model.ts

Feature Path: src/modules/account/account.model.ts

import { Schema, model, Types } from "mongoose";
import {
  ACCOUNT_CURRENCIES,
  ACCOUNT_STATUS,
  ACCOUNT_TYPES
} from "../constants/account";
import { AccountCurrency, AccountStatus, AccountType } from "../types/account";
 
export interface IAccount {
  _id: Types.ObjectId;
  userId: Types.ObjectId;
  type: AccountType;
  currency: AccountCurrency;
  status: AccountStatus;
 
  systemAccount: boolean;
 
  createdAt: Date;
  updatedAt: Date;
}
 
const AccountSchema = new Schema<IAccount>(
  {
    userId: {
      type: Schema.Types.ObjectId,
      ref: "User",
      required: true,
      index: true
    },
    type: {
      type: String,
      enum: ACCOUNT_TYPES,
      required: true
    },
    currency: {
      type: String,
      enum: ACCOUNT_CURRENCIES,
      default: "NPR"
    },
    status: {
      type: String,
      enum: ACCOUNT_STATUS,
      default: "active",
      index: true
    },
    systemAccount: {
      type: Boolean,
      default: false,
      select: false,
      immutable: true
    }
  },
  { timestamps: true }
);
 
const Account = model<IAccount>("Account", AccountSchema);
 
export default Account;

Installation

npx servercn-cli add schema banking-app/account

3. Transaction Schema

The Transaction schema records money movements between accounts, including transfers, deposits, and withdrawals.

MVC Path: src/models/transaction.model.ts

Feature Path: src/modules/transaction/transaction.model.ts

import { Schema, model, Types } from "mongoose";
import { TRANSACTION_STATUS } from "../constants/account";
import { TransactionStatus } from "../types/account";
 
export interface ITransaction {
  _id: Types.ObjectId;
 
  fromAccountId: Types.ObjectId;
  toAccountId: Types.ObjectId;
 
  amount: number;
  status: TransactionStatus;
  idempotencyKey: string;
 
  createdAt: Date;
  updatedAt: Date;
}
 
const TransactionSchema = new Schema<ITransaction>(
  {
    fromAccountId: {
      type: Schema.Types.ObjectId,
      ref: "Account",
      required: true,
      index: true
    },
    toAccountId: {
      type: Schema.Types.ObjectId,
      ref: "Account",
      required: true,
      index: true
    },
    amount: {
      type: Number,
      required: true
    },
    status: {
      type: String,
      enum: TRANSACTION_STATUS,
      default: "pending"
    },
    idempotencyKey: {
      type: String,
      required: true,
      unique: true,
      index: true
    }
  },
  { timestamps: true }
);
 
const Transaction = model<ITransaction>("Transaction", TransactionSchema);
 
export default Transaction;

Installation

npx servercn-cli add schema banking-app/transaction

5. Ledger Schema

The Ledger schema records all financial movements such as deposits, withdrawals, and transfers. Each entry represents a finalized financial event.

Ledger entries are immutable. All update and delete operations are blocked to preserve audit integrity.

Path

  • MVC: src/models/ledger.model.ts
  • Feature: src/modules/ledger/ledger.model.ts
import { Schema, model, Types } from "mongoose";
import { LedgerEntryType } from "../types/account";
import { LEDGER_ENTRY_TYPES } from "../constants/account";
import { ApiError } from "../utils/api-error";
 
export interface ILedger {
  _id: Types.ObjectId;
 
  accountId: Types.ObjectId;
  transactionId: Types.ObjectId;
  entryType: LedgerEntryType;
  amount: number;
 
  createdAt: Date;
  updatedAt: Date;
}
 
const LedgerSchema = new Schema<ILedger>(
  {
    accountId: {
      type: Schema.Types.ObjectId,
      ref: "Account",
      index: true,
      immutable: true
    },
    transactionId: {
      type: Schema.Types.ObjectId,
      ref: "Transaction",
      index: true,
      immutable: true
    },
    entryType: {
      type: String,
      enum: LEDGER_ENTRY_TYPES,
      required: true,
      immutable: true
    },
    amount: {
      type: Number,
      required: true,
      immutable: true
    }
  },
  { timestamps: true }
);
 
function preventLedgerUpdate() {
  return ApiError.badRequest("Ledger cannot be updated");
}
 
//? Pre hooks to prevent ledger update
LedgerSchema.pre("findOneAndDelete", preventLedgerUpdate);
LedgerSchema.pre("findOneAndReplace", preventLedgerUpdate);
LedgerSchema.pre("findOneAndUpdate", preventLedgerUpdate);
LedgerSchema.pre("updateOne", preventLedgerUpdate);
LedgerSchema.pre("updateMany", preventLedgerUpdate);
LedgerSchema.pre("deleteMany", preventLedgerUpdate);
LedgerSchema.pre("deleteOne", preventLedgerUpdate);
 
export const Ledger = model<ILedger>("Ledger", LedgerSchema);

Installation

npx servercn-cli add schema banking-app/ledger

Notes:

  • Ledger entries are immutable
  • All update and delete operations are blocked to preserve audit integrity

File & Folder Structure

ServerCN

Select a file to view its contents

Installation

npx servercn-cli add schema banking-app