Skip to content

dev-koushal/CreditSea-Assignment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 

Repository files navigation

CreditSea Assignment — Project Overview and Architecture

This README explains the CreditSea Assignment project in simple language and with clear examples. It covers what the system does, the technologies used, how each logic block is implemented (including the Business Rule Engine, or BRE), how the dashboards work, and how to run the project locally.

The goal is to teach a developer or reviewer how the app is structured, why design choices were made, and how to operate the main flows. This document uses plain language and many bullets and examples for clarity.


Table of contents

  • Project summary
  • Technologies used
  • High-level architecture and data flow
  • Business Rule Engine (BRE): what, why, and where it runs
  • Server-side logic: models, services, controllers, and routes
  • Client-side logic: pages, components, and dashboards
  • Dashboard explanations (module by module)
    • Sales
    • Sanction
    • Disbursement
    • Collection
    • Borrower
  • Example flows (apply -> sanction -> loan -> disburse -> repay)
  • Error handling and edge cases
  • Best practices and architectural notes
  • How to run locally (quick start)

Project summary

CreditSea is a small loan processing app with two main parts:

  • A server (API) built with TypeScript + Express with MongoDB for persistence.
  • A client (Next.js + React + Tailwind) for user interfaces and dashboards.

The app supports a borrower creating an application, uploading documents, and several internal modules (Sales, Sanction, Disbursement, Collection) each having a dashboard where staff perform actions. The server contains a Business Rule Engine (BRE) that decides eligibility and status transitions.

This README explains the code and architecture so you can understand, extend, and run the project.


Technologies used

  • Node.js and npm — runtime and package management.
  • TypeScript — typed JavaScript for both server and client.
  • Express — server framework for APIs.
  • MongoDB (mongoose) — document database; models are defined with Mongoose.
  • Next.js + React — client UI and routing.
  • Tailwind CSS — utility-first CSS framework for styling.
  • Multer — file upload handling on the server (salary slips).
  • Zod — validation library for incoming application input.

Why these choices?

  • TypeScript improves code clarity and prevents common runtime errors.
  • Express is lightweight and fits the API-first approach.
  • MongoDB is flexible for the domain model; mongoose keeps models and schemas simple.
  • Next.js makes it easy to build a React app with routing and server-side capabilities if needed.

High-level architecture and data flow

  1. The browser (client) requests pages and calls API endpoints on the server.
  2. Authentication is cookie-based; protected endpoints use middleware that attaches auth information to the request.
  3. Borrowers create Application documents on the server. The Application has a status (ELIGIBLE, PENDING, APPROVED, REJECTED).
  4. The BRE (Business Rule Engine) evaluates application data when created, setting ELIGIBLE or REJECTED.
  5. If a borrower uploads documents (salary slip), the application may move from ELIGIBLE to PENDING for human review.
  6. Sanction users review PENDING applications; on approval a Loan document is created and the Application moves to APPROVED.
  7. Disbursement users mark Loan documents as disbursed, moving the loan to ACTIVE.
  8. Collection users or borrowers make repayments; repayments update Loan.outstandingBalance and may close the loan.

Data model (simplified):

  • User: { email, passwordHash, role }
  • Application: { borrowerId, fullName, pan, dob, monthlySalary, amount, tenureDays, employmentMode, status, reason, salarySlipPath }
  • Loan: { borrowerId, amount, tenureDays, interestRate, simpleInterest, outstandingBalance, status, sanctionedBy, disbursedBy }
  • Repayment: { loanId, utr, amount, createdAt }

Status transitions (summary):

  • Application: ELIGIBLE -> PENDING -> APPROVED or REJECTED
  • Loan: PENDING -> SANCTIONED -> DISBURSED -> ACTIVE -> CLOSED

Business Rule Engine (BRE): what, why, and where it runs

What is the BRE?

  • The BRE is the collection of rules that determine whether an application is automatically rejected, considered eligible, or needs manual review.

Why use a BRE?

  • Make eligibility decisions consistent and auditable.
  • Allow complex business logic to be centralized and easy to change.
  • Keep validation and business policy out of the UI so rules do not leak into many places.

Where is the BRE implemented in this project?

  • The BRE is implemented on the server inside server/src/services/applicationService.ts.
  • Example rules implemented:
    • Age must be between 23 and 50.
    • Monthly salary must be >= ₹25,000.
    • Unemployed applicants are rejected.

Why on the server and not only in the client?

  • Server-side enforcement is authoritative; clients can be tampered with.
  • Keeping BRE on the server ensures rules apply no matter how the client behaves (web, API, or automated scripts).
  • The client may run a small subset of the rules for better UX (e.g., form validation) but the server must be the source of truth.

Should components of BRE live on the client as well?

  • Some client-side checks are helpful to provide immediate feedback (e.g., required fields, basic format checks). Those are not replacements for server BRE.
  • The server BRE contains the canonical rules; small client-side rules duplicate behavior for UX only.

Summary recommendation:

  • Keep authoritative rules on the server. Mirror lightweight checks on the client purely for UX.
  • If you need more advanced BRE features (rule versions, toggles), consider a configuration-driven rules engine or storing rules in a JSON format that the server reads.

Server-side logic: models, services, controllers, and routes

The server follows a layered architecture:

  • Models: Mongoose models under server/src/models (Application, Loan, Repayment, User).
  • Services: Business logic and data orchestration under server/src/services (applicationService, loanService, etc.). BRE rules live here.
  • Controllers: HTTP handlers under server/src/controllers that validate auth, extract inputs, call services, and send responses.
  • Routes: Express route definitions under server/src/routes that map endpoints to controllers.
  • Middleware: Authentication and error handling under server/src/middleware.

Why this separation?

  • Controllers stay thin and focus on HTTP concerns (status codes, request/response).
  • Services contain the actual logic that can be unit tested without HTTP.
  • Models encapsulate data shape and persistence logic.

Important server flow examples

  1. Create application (Borrower)

    • Route: POST /applications
    • Controller: parses and validates input with Zod, then calls applicationService.createApplication.
    • Service: runs BRE rules, creates the Application in DB, returns the created document.
  2. Sanction action (Sanction user)

    • Route: POST /loans/:loanId/sanction (the code expects an application id here — the naming is historical but behavior uses Application id)
    • Controller: authorizes role, reads action (approve/reject), calls loanService.sanctionLoan.
    • Service: if approve → creates a Loan and sets Application.status = APPROVED; if reject → sets Application.status = REJECTED and saves reason.
  3. Disburse action (Disbursement user)

    • Route: POST /loans/:loanId/disburse
    • Service: set loan status to DISBURSED then ACTIVE, set disbursedAt and disbursedBy.
  4. Repayment

    • Route: POST /loans/:loanId/repay
    • Service: validates loan status, creates Repayment record, updates Loan.outstandingBalance, mark loan CLOSED if fully paid.

Notes about error handling

  • The server uses a global error handler middleware. Services throw errors; controllers forward them to the handler.
  • Controllers should return meaningful HTTP status codes (400 for bad input, 401 for auth, 403 for forbidden).

Client-side logic: pages, components, and dashboards

Project structure (client):

  • client/pages — Next.js pages, including dashboards under client/pages/dashboard.
  • client/components — shared components like AuthProvider.
  • client/styles — Tailwind and global CSS.

How the client talks to the server

  • API base URL is process.env.NEXT_PUBLIC_API_URL with a default of http://localhost:4000.
  • Calls include credentials: 'include' to allow cookie-based authentication.

Auth flow

  • There's an AuthProvider component that stores user and logout and provides it to pages via React context.
  • Dashboard pages check user.role to allow or deny access on the client side.

Why check roles on the client?

  • This is for UX (hide/redirect unauthorized UI). The server also enforces role-based access with middleware. Never rely on client checks alone for security.

Major client pages and purpose

  • pages/dashboard/sales.tsx — shows leads for sales users.
  • pages/dashboard/sanction.tsx — shows pending applications for sanction users; has Approve/Reject actions.
  • pages/dashboard/disbursement.tsx — shows sanctioned loans for disbursement users; action to mark disbursed.
  • pages/dashboard/collection.tsx — shows active loans for collection; actions to record repayments.
  • pages/borrower/* — application creation and salary slip upload pages for borrowers.

Client responsibilities vs server responsibilities

  • Client: UI, input validation (for UX), navigation, user feedback, and calling authenticated endpoints.
  • Server: BRE, data integrity, authorization, business logic, and persistence.

Dashboard explanations: heading, subheading, what, why, how it does

Below each module is explained: what it shows, why it exists, and exactly how it works in the code.

Sales Dashboard

  • What it shows:

    • Leads: users who are borrowers but have not created an application.
  • Why it exists:

    • Sales staff track potential borrowers and encourage them to apply.
  • How it does it (in code):

    • The server endpoint GET /loans/module/sales (controller listForModule) queries User and Application collections.
    • It finds borrower users and then filters out those who already have an Application document.
    • Client renders these leads and provides short actions like "Contact" or "Create application" (if implemented).
  • Example:

    • The controller builds a set of borrower IDs that have applications, then returns other borrowers as leads.

Sanction Dashboard

  • What it shows:

    • Applications with status: PENDING that require manual review.
  • Why it exists:

    • Some applicants are automatically rejected or marked ELIGIBLE by the BRE. Others need human review because the system needs documents or verification.
  • How it does it (in code):

    • Client calls GET /loans/module/sanction and the server returns Application.find({ status: 'PENDING' }).
    • Approve action: Client posts POST /loans/:applicationId/sanction with { action: 'approve' }. The server runs loanService.sanctionLoan which:
      • Validates application is PENDING.
      • Sets Application.status = APPROVED.
      • Creates a Loan document with computed interest and repayment amounts.
      • Returns both the loan and the updated application.
    • Reject action: Client posts POST /loans/:applicationId/sanction with { action: 'reject', reason }. The server sets Application.status = REJECTED and stores the reason.
  • Example UI flow (approve):

    1. Sanction user clicks Approve.
    2. Client sends request to sanction endpoint.
    3. Server creates Loan and returns result.
    4. Client refetches the pending list and the item disappears.

Disbursement Dashboard

  • What it shows:

    • Loans that have been sanctioned and are waiting to be released (status SANCTIONED).
  • Why it exists:

    • To separate duties: sanction approves creditworthiness and terms; disbursement performs fund release and records payment details.
  • How it does it (in code):

    • Client calls GET /loans/module/disbursement to get loans with LoanStatus.SANCTIONED.
    • Disbursement user clicks Disburse which calls POST /loans/:loanId/disburse.
    • Server marks loan DISBURSED and ACTIVE, sets disbursedBy and disbursedAt.
  • Notes:

    • In production, disbursement would integrate with payment rails and more checks.

Collection Dashboard

  • What it shows:

    • Loans in ACTIVE or DISBURSED state which may need repayments.
  • Why it exists:

    • Collection staff track repayments, reconcile UTRs, and follow up on late payments.
  • How it does it (in code):

    • Client calls GET /loans/module/collection to get loans with status ACTIVE or DISBURSED.
    • When recording payment, client calls POST /loans/:loanId/repay with { utr, amount }.
    • Server creates a Repayment record and reduces Loan.outstandingBalance. If outstanding balance reaches zero, Loan becomes CLOSED.
  • Edge cases handled:

    • Reject payments that exceed outstanding balance.
    • Ensure unique UTRs per Repayment (model-level constraint).

Borrower dashboard and pages

  • What it shows:

    • Borrower can create an application and upload salary slips.
    • applications/me returns the borrower's applications and their status.
  • Why it exists:

    • Borrower-facing flow is needed to collect information and documents.
  • How it does it (in code):

    • POST /applications creates an application. Service runs BRE rules and sets initial status.
    • POST /applications/:id/salary-slip is a file upload endpoint using Multer. It saves a file path on the application and may promote ELIGIBLE -> PENDING.
  • Important UX note:

    • Client shows application.status in the UI so borrowers can track progress.

Example flows (detailed)

I will explain one full example: from application submission to repayment, with snippets and expected server behavior.

  1. Borrower submits application
  • Client request:
POST /applications
Content-Type: application/json
Cookie: session=...

{
	"fullName": "Rina Sharma",
	"pan": "ABCDE1234F",
	"dob": "1990-04-12",
	"monthlySalary": 30000,
	"amount": 150000,
	"tenureDays": 180,
	"employmentMode": "Salaried"
}
  • Server behavior:
    • borrowerController.createApplication validates with Zod.
    • applicationService.createApplication runs BRE:
      • Age is within 23-50, salary >= 25000, so status becomes ELIGIBLE.
    • Save Application with status: ELIGIBLE.
    • Return the created application to client.
  1. Borrower uploads salary slip (optional but common)
  • Client uses POST /applications/:id/salary-slip with multipart/form-data containing the file.
  • Server stores file under uploads/ and sets application.salarySlipPath.
  • Service moves ELIGIBLE -> PENDING so human review is required.
  1. Sanction user approves the application
  • Client (sanction dashboard) calls:
POST /loans/:applicationId/sanction
Content-Type: application/json
Cookie: session=...

{ "action": "approve" }
  • Server behavior:
    • Validate role (Sanction or Admin).
    • Ensure application status is PENDING.
    • Set Application.status = APPROVED.
    • Create Loan document with computed interest and total repayment fields.
    • Return loan and updated application.
  1. Disbursement user releases funds
  • Client calls:
POST /loans/:loanId/disburse
Cookie: session=...
  • Server marks loan DISBURSED and ACTIVE, timestamps disbursedAt, and sets disbursedBy.
  1. Repayment
  • Client (collection dashboard or borrower) calls:
POST /loans/:loanId/repay
Content-Type: application/json
Cookie: session=...

{ "utr": "UTR12345678", "amount": 5000 }
  • Server validates loan is ACTIVE or DISBURSED.
  • Creates Repayment record and adjusts outstanding balance.
  • If outstanding becomes 0 → loan CLOSED.

Error handling and edge cases

  • The server returns clear status codes and messages for common problems:

    • 400 Bad Request: invalid input or missing parameters.
    • 401 Unauthorized: no valid auth information.
    • 403 Forbidden: user role not allowed for the action.
    • 500 Internal Server Error: unexpected failures.
  • Edge cases handled in code:

    • Unique UTR enforcement for repayments.
    • Rejecting sanction actions when application status is not PENDING.
    • Rejecting disbursement if loan is not SANCTIONED.
    • Prevent creating duplicate loans for the same application.

Best practices and architectural notes (teaching)

  • Keep business rules on the server. Use the client only for UX safety checks.
  • Keep controllers thin. Put logic in services so it can be tested independently of HTTP.
  • Use enums for status values (TypeScript enum), and reference them everywhere to avoid typos.
  • Use explicit status transitions and validate current state before moving to the next.
  • Use createdAt / updatedAt timestamps on records to help audit activity.
  • If the BRE grows, externalize rules to configuration files or a dedicated rule service. This allows rule changes without code changes.
  • Log important actions (sanctioned by, disbursed by, repayment utr) for traceability.

Scaling suggestions

  • If the project grows, split services into microservices (auth, loans, payments) and use an event queue for state transitions and auditing.
  • Add an async worker for sending notifications, reconciling repayments, and heavy tasks like document OCR.

How to run locally (quick start)

Prerequisites:

  • Node.js (v16+ recommended)
  • npm
  • MongoDB running (local or remote)

Environment variables (create a .env file in the server folder):

  • MONGO_URI — MongoDB connection string. Example: mongodb://localhost:27017/creditsea
  • JWT_SECRET — a string used to sign session tokens. Example: devsecret
  • PORT — optional server port (default: 4000)

Steps to run server and client:

  1. Start MongoDB (if local):
# depending on your setup, for example:
mongod --dbpath /data/db
  1. Start the server
cd server
npm install
npm run dev
  1. Seed example users (optional) — there is a script in server/scripts/seed.js that creates sample users like [email protected] and others. To run it:
cd server
node scripts/seed.js
  1. Start the client
cd client
npm install
npm run dev
  1. Open the app in the browser:
  • Client at: http://localhost:3000
  • Server health: http://localhost:4000/health
  1. Common test accounts (from seed):
  • Sanction: [email protected] / sanction123
  • Use the signup page to create other roles, or run seed script.

Notes on development

  • If you change server TypeScript files, npm run dev should restart the server if ts-node-dev or equivalent is configured. If you see no changes, restart manually.
  • The server serves uploaded files under /uploads and the client may link to them directly.

Closing notes

This README gives you a clear map of how the CreditSea Assignment project is built and why it is structured this way. It focuses on keeping business logic safely on the server (BRE), using simple and clear status transitions, and separating responsibilities between client and server.

If you want, I can also:

  • Add a simple architecture diagram (ASCII or Mermaid) saved in this repo.
  • Add an automated Postman collection or example curl commands for testing the main flows.
  • Add unit tests for key services (BRE and sanction flow).

Tell me which of these you'd like next and I will implement it.

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors