Digital Lending Journey for all loan types Retail/SME/Agri-rural/education/green loan
- Anand Nerurkar
- Sep 30
- 8 min read
Digital lending journey is not re-written for each loan type — we design a core reusable skeleton, and then enable/disable steps based on loanType + productConfig + rules.
Let’s define this properly:
🔹 Core Digital Lending Journey (Common to All)
Loan Application Submission
Borrower submits application (UI → Loan Service).
Captures borrower info, loan type, product code.
Persisted in loan_application (Postgres) with correlationId + idempotencyKey.
Outbox → event LOAN_INITIATED.
Consent & Document Upload
eConsent (data sharing, credit bureau access, AML checks).
Upload KYC docs, income proofs, collateral papers.
Events: CONSENT_GIVEN, DOC_UPLOADED.
Parallel Verification Services
KYC Service: Aadhaar/PAN/Passport → KYC_REQUESTED, KYC_DONE/KYC_FAILED.
Credit Service: Bureau check, alternate scoring → CREDIT_REQUESTED, CREDIT_DONE/CREDIT_FAILED.
Fraud/AML/FinCrime Services: Blacklists, suspicious activity → REQUESTED, DONE/*_FAILED.
Collateral/Property Service (conditional): Valuation, legal, insurance.
RTO/Dealer APIs (conditional): Vehicle ownership for auto loans.
GST/Bank Statement Analytics (conditional): SME/MSME loans.
Aggregation & Decisioning
Orchestrator waits for all required events (depends on loan type).
Runs Drools Excel-based decision table (business editable).
Emits: LOAN_APPROVED, LOAN_REJECTED, LOAN_MANUAL_REVIEW.
Loan Agreement & e-Sign
eMandate for EMI deduction.
Digital signature of agreement.
Disbursement
Core banking integration.
Events: DISBURSEMENT_INITIATED, DISBURSEMENT_DONE, DISBURSEMENT_PARTIAL (e.g. home loan staged disbursements).
Post-Disbursement Monitoring
For BNPL/Consumer Durable: repayment tracking, zero-cost EMI.
For SME/Agri: seasonal repayment tracking.
For Corporate: milestone-based monitoring.
🔹 Variations by Loan Type (Additional Steps)
1. Retail / Consumer Loans
Personal Loan: skip collateral; instant disbursement possible after e-KYC + alternate scoring.
Auto Loan: add RTO check, dealer/insurance integration.
Home Loan: add property verification + valuation + legal.
BNPL: lightweight KYC, instant bureau/alternate scoring; minimal documents.
2. SME / MSME Loans
Working Capital / Term Loan: add GST analytics, cash flow analysis.
Invoice Discounting: add invoice upload + buyer confirmation.
Merchant Cash Advance: add POS/UPI transaction data integration.
3. Agri & Rural Loans
Crop Loan / KCC: add land records check + seasonal repayment config.
Equipment Financing: add dealer + subsidy scheme integration.
4. Corporate / Institutional Loans
Project Finance: add multi-party approval workflow; milestone-based disbursement.
Syndicated Loan: add multi-lender participation workflow.
Trade Finance / LC: add customs/trade APIs, bank guarantee checks.
5. Specialized / Emerging Products
Education Loan: add co-applicant consent, university admission verification.
Green Loans: add renewable project validation, subsidy check.
Microfinance: add group lending + community verification.
P2P Lending: add platform-level matching engine + lender consent.
🔹 How We Enable / Disable Steps
Product Config Table (product_config):
loan_type = HOME_RESALE → requires propertyVerification=true, legalOpinion=true.
loan_type = AUTO → requires rtoCheck=true, dealerIntegration=true.
loan_type = PERSONAL → requires collateral=false.
Rules Engine (Drools Excel):
Maps credit score, fraud, income/EMI ratio → APPROVED, REJECTED, REVIEW.
Event-driven choreography:
Each microservice listens to loan-status (LOAN_INITIATED).
Based on product_config, orchestrator waits for only those checks.
✅ So the digital lending journey is ONE skeleton, and loan type decides extra hops.For example:
Amit R → Personal Loan: skip property/legal. Approved in < 10 mins.
Rama → Auto Loan: after credit + KYC, also need RTO + insurance.
Shyam → Home Loan (Resale): adds property + legal + valuation.
Radha → Education Loan: adds co-applicant consent + university validation.
Perfect 👍 Let’s do this step-by-step.I’ll show the full text event sequence (UI → DB → Outbox → Kafka → Services → Decision) for each loan type.We’ll reuse the same skeleton, but highlight where steps differ.
🔹 1. Amit R – Personal Loan (Unsecured)
Flow:
UI → Loan Service
Insert into loan_application (loanType=PERSONAL).
Insert into outbox_event → event_type=LOAN_INITIATED, status=PENDING.
Poller → Kafka
Publishes LOAN_INITIATED to topic loan-status.
Consumers
KYC Service → inserts KYC_REQUESTED → after Aadhaar → KYC_DONE.
Credit Service → CREDIT_REQUESTED → fetch bureau → CREDIT_DONE (score=750).
Fraud Service → FRAUD_CHECK_REQUESTED → ML → FRAUD_DONE.
AML Service → AML_REQUESTED → FATCA/OFAC → AML_DONE.
Orchestrator
Waits for [KYC_DONE, CREDIT_DONE, FRAUD_DONE, AML_DONE].
Runs Drools → approves.
Inserts into outbox → LOAN_APPROVED.
Poller → publishes LOAN_APPROVED to loan-status.
Disbursement Service
Consumes LOAN_APPROVED.
Calls CBS → disburses → DISBURSEMENT_DONE.
🔹 2. Rama – Auto Loan (Secured)
Flow:
Loan Service: LOAN_INITIATED.
Parallel Services
KYC → KYC_DONE.
Credit → CREDIT_DONE.
Fraud/AML → DONE.
RTO Service → RTO_CHECK_REQUESTED → RTO_VERIFIED.
Insurance Service → INSURANCE_CHECK_REQUESTED → INSURANCE_VERIFIED.
Orchestrator
Waits for [KYC, Credit, Fraud, AML, RTO_VERIFIED, INSURANCE_VERIFIED].
Runs Drools (loan-to-value ratio, income/EMI).
Inserts → LOAN_APPROVED.
Disbursement
Dealer account credited.
Event: DISBURSEMENT_DONE.
🔹 3. Shyam – Home Loan (Resale Property)
Flow:
Loan Service: LOAN_INITIATED.
Parallel Services
KYC, Credit, Fraud, AML → same.
Property Service → PROPERTY_VERIFICATION_REQUESTED → PROPERTY_VERIFIED.
Valuation Service → VALUATION_REQUESTED → VALUATION_DONE.
Legal Service → LEGAL_OPINION_REQUESTED → LEGAL_OPINION_DONE.
Orchestrator
Waits for [KYC, Credit, Fraud, AML, PROPERTY_VERIFIED, VALUATION_DONE, LEGAL_OPINION_DONE].
Runs Drools (LTV ratio, legal status).
Inserts → LOAN_APPROVED.
Disbursement
Staged disbursement: DISBURSEMENT_PARTIAL events.
🔹 4. Radha – Education Loan
Flow:
Loan Service: LOAN_INITIATED.
Parallel Services
KYC, Credit, Fraud, AML.
Co-Applicant Service → COAPPLICANT_CONSENT_REQUESTED → CONSENT_DONE.
University Validation Service → UNIVERSITY_CHECK_REQUESTED → UNIVERSITY_VERIFIED.
Orchestrator
Waits for [KYC, Credit, Fraud, AML, COAPPLICANT_CONSENT, UNIVERSITY_VERIFIED].
Rule check (parent income, university rating).
Emits LOAN_APPROVED.
Disbursement
Direct to university or staged per semester → multiple DISBURSEMENT_PARTIAL.
🔹 5. Ramesh – Consumer Durable / BNPL Loan
Flow:
Loan Service: LOAN_INITIATED.
Parallel Services
e-KYC → instant OTP-based → KYC_DONE.
Alternate credit scoring (mobile data, payments history) → CREDIT_DONE.
Fraud light check.
Orchestrator
Only needs [KYC_DONE, CREDIT_DONE].
Approves instantly.
Disbursement
Merchant settlement instantly.
Event: DISBURSEMENT_DONE.
✅ Key Insight:
Same skeleton journey (Application → Verification → Decision → Disbursement).
Loan Type → decides which extra services must publish DONE events before Orchestrator approves.
Kafka Topics remain generic:
loan-status → loan events
kyc-status → kyc events
credit-status → credit events
etc.
Postgres table schemas (loan_application, outbox_event, consent, loan_document, loan_state) and show how each event updates these tables step by step for (say Amit R’s personal loan)? That would give you DB + event trace together.
Perfect 👍 Let’s take Amit R – Personal Loan and walk through the full DB + Outbox + Kafka trace.I’ll define the Postgres tables first, then show how rows evolve step by step as Amit’s journey progresses.
🗂️ Postgres Schema
1. Loan Application
CREATE TABLE loan_application (
loan_id UUID PRIMARY KEY,
customer_id UUID NOT NULL,
loan_type VARCHAR(50) NOT NULL,
amount NUMERIC(15,2),
status VARCHAR(50) NOT NULL, -- e.g. INITIATED, APPROVED, REJECTED
created_at TIMESTAMP DEFAULT now(),
updated_at TIMESTAMP DEFAULT now()
);
2. Outbox Event
CREATE TABLE outbox_event (
event_id UUID PRIMARY KEY,
loan_id UUID,
event_type VARCHAR(100) NOT NULL, -- e.g. LOAN_INITIATED, KYC_DONE
payload JSONB,
status VARCHAR(20) DEFAULT 'PENDING', -- PENDING/PROCESSED
created_at TIMESTAMP DEFAULT now()
);
3. Consent
CREATE TABLE consent (
consent_id UUID PRIMARY KEY,
loan_id UUID,
service_name VARCHAR(50), -- e.g. KYC, Credit, Fraud
status VARCHAR(20), -- REQUESTED, DONE, FAILED
created_at TIMESTAMP DEFAULT now(),
updated_at TIMESTAMP DEFAULT now()
);
4. Loan Document
CREATE TABLE loan_document (
document_id UUID PRIMARY KEY,
loan_id UUID,
doc_type VARCHAR(50), -- Aadhaar, PAN, BankStatement, etc.
status VARCHAR(20), -- UPLOADED, VERIFIED, REJECTED
created_at TIMESTAMP DEFAULT now(),
updated_at TIMESTAMP DEFAULT now()
);
5. Loan State (latest snapshot)
CREATE TABLE loan_state (
loan_id UUID PRIMARY KEY,
current_state VARCHAR(100), -- e.g. KYC_DONE, FRAUD_DONE
last_updated TIMESTAMP DEFAULT now()
);
🔹 Step-by-Step Journey – Amit R (Personal Loan)
Step 1: Loan Initiation
Insert loan
INSERT INTO loan_application (loan_id, customer_id, loan_type, amount, status)
VALUES ('L001', 'C001', 'PERSONAL', 500000, 'INITIATED');
Outbox entry
INSERT INTO outbox_event (event_id, loan_id, event_type, payload)
VALUES ('E001', 'L001', 'LOAN_INITIATED', '{"amount":500000,"loanType":"PERSONAL"}');
📤 Poller picks → Kafka → Topic loan-status → Event: LOAN_INITIATED.
Step 2: KYC Service
Insert consent
INSERT INTO consent (consent_id, loan_id, service_name, status)
VALUES ('CN001', 'L001', 'KYC', 'REQUESTED');
After Aadhaar validation
UPDATE consent SET status='DONE' WHERE loan_id='L001' AND service_name='KYC';
Outbox event
INSERT INTO outbox_event (event_id, loan_id, event_type, payload)
VALUES ('E002', 'L001', 'KYC_DONE', '{"aadhaar":"xxxx","pan":"xxxx"}');
📤 Kafka → Topic kyc-status → Event: KYC_DONE.
Step 3: Credit Service
INSERT INTO consent (consent_id, loan_id, service_name, status)
VALUES ('CN002', 'L001', 'CREDIT', 'REQUESTED');
UPDATE consent SET status='DONE' WHERE loan_id='L001' AND service_name='CREDIT';
INSERT INTO outbox_event (event_id, loan_id, event_type, payload)
VALUES ('E003', 'L001', 'CREDIT_DONE', '{"score":750}');
📤 Kafka → Topic credit-status.
Step 4: Fraud Service
INSERT INTO consent (consent_id, loan_id, service_name, status)
VALUES ('CN003', 'L001', 'FRAUD', 'REQUESTED');
UPDATE consent SET status='DONE' WHERE loan_id='L001' AND service_name='FRAUD';
INSERT INTO outbox_event (event_id, loan_id, event_type, payload)
VALUES ('E004', 'L001', 'FRAUD_DONE', '{"risk":"LOW"}');
📤 Kafka → Topic fraud-status.
Step 5: AML Service
INSERT INTO consent (consent_id, loan_id, service_name, status)
VALUES ('CN004', 'L001', 'AML', 'REQUESTED');
UPDATE consent SET status='DONE' WHERE loan_id='L001' AND service_name='AML';
INSERT INTO outbox_event (event_id, loan_id, event_type, payload)
VALUES ('E005', 'L001', 'AML_DONE', '{"watchlist":"CLEAR"}');
📤 Kafka → Topic aml-status.
Step 6: Orchestrator → Loan Decision
Orchestrator waits until KYC, CREDIT, FRAUD, AML are DONE.
Then updates loan:
UPDATE loan_application SET status='APPROVED' WHERE loan_id='L001';
Insert event:
INSERT INTO outbox_event (event_id, loan_id, event_type, payload)
VALUES ('E006', 'L001', 'LOAN_APPROVED', '{"approvedAmount":500000}');
📤 Kafka → Topic loan-status.
Step 7: Disbursement
CBS called, money transferred.
INSERT INTO outbox_event (event_id, loan_id, event_type, payload)
VALUES ('E007', 'L001', 'DISBURSEMENT_DONE', '{"txnId":"TXN123","amount":500000}');
📤 Kafka → Topic disbursement-status.
✅ Trace Summary for Amit R
Loan Application → LOAN_INITIATED
KYC → KYC_DONE
Credit → CREDIT_DONE
Fraud → FRAUD_DONE
AML → AML_DONE
Orchestrator → LOAN_APPROVED
Disbursement → DISBURSEMENT_DONE
Every step:
Row in consent/loan_application/loan_state.
Row in outbox_event.
Published to Kafka.
DB + Event Trace style walkthrough for the other journeys (Auto, Home, Education, BNPL) as well, or should I also give you a small runnable Spring Boot + Kafka POC codebase skeleton that implements this pattern?
Got it ✅Let’s do both for you in structured way:
🏦 1. Digital Lending Journey – Multiple Loan Types
We keep the same pattern (DB → Outbox → Poller → Kafka → Consumer → Next Step) for all loan types.Differences come from what extra checks/services are involved.
🔹 Personal Loan (Amit R – already shown in detail)
Steps: Loan Initiation → KYC → Credit → Fraud → AML → Decision → Disbursement.Events: LOAN_INITIATED, KYC_DONE, CREDIT_DONE, FRAUD_DONE, AML_DONE, LOAN_APPROVED, DISBURSEMENT_DONE.
🔹 Auto Loan / Two-Wheeler Loan
Extra services: RTO check, Insurance validation.Events:
LOAN_INITIATED
KYC_DONE
CREDIT_DONE
RTO_VERIFIED
INSURANCE_VERIFIED
LOAN_APPROVED
DISBURSEMENT_DONE
🔹 Home Loan
LOAN_INITIATED
KYC_DONE
CREDIT_DONE
PROPERTY_VALUATION_DONE
LEGAL_OPINION_DONE
LOAN_APPROVED
DISBURSEMENT_DONE
🔹 Consumer Durable Loan / BNPL
Lightweight – only basic checks.Events:
LOAN_INITIATED
KYC_DONE
ALTERNATE_CREDIT_SCORE_DONE
LOAN_APPROVED
DISBURSEMENT_DONE
🔹 SME Loan (Working Capital)
LOAN_INITIATED
KYC_DONE
GST_VERIFIED
BANK_STATEMENT_ANALYZED
CREDIT_DONE
LOAN_APPROVED
DISBURSEMENT_DONE
🔹 Agri Loan (Crop Loan)
LOAN_INITIATED
KYC_DONE
LAND_RECORD_VERIFIED
SUBSIDY_ELIGIBILITY_DONE
LOAN_APPROVED
DISBURSEMENT_DONE
🔹 Education Loan
LOAN_INITIATED
KYC_DONE
CO_BORROWER_KYC_DONE
UNIVERSITY_VERIFIED
CREDIT_DONE
LOAN_APPROVED
DISBURSEMENT_DONE
🔹 Green Loan
Extra: Project Verification (e.g. Solar, EV).Events:
LOAN_INITIATED
KYC_DONE
PROJECT_VERIFIED
CREDIT_DONE
LOAN_APPROVED
DISBURSEMENT_DONE
🔹 Corporate Loan (Project Finance)
Extra: Syndication, Collateral Valuation, Legal & Compliance.Events:
LOAN_INITIATED
KYC_DONE
COLLATERAL_VALUATION_DONE
LEGAL_COMPLIANCE_DONE
SYNDICATION_APPROVED
LOAN_APPROVED
DISBURSEMENT_DONE
🛠️ 2. Runnable Spring Boot + Kafka POC (Skeleton)
I’ll keep this light but production-style so you can extend.We’ll focus on Loan Orchestrator + Outbox + Poller + Event Publishing.
@Entity
@Table(name = "loan_application")
public class LoanApplication {
@Id
private UUID loanId;
private String customerId;
private String loanType;
private BigDecimal amount;
private String status; // INITIATED, APPROVED, REJECTED
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@Entity
@Table(name = "outbox_event")
public class OutboxEvent {
@Id
private UUID eventId;
private UUID loanId;
private String eventType; // e.g. LOAN_INITIATED, KYC_DONE
private String payload; // JSON
private String status; // PENDING, PROCESSED
private LocalDateTime createdAt;
}
@Service
public class LoanService {
private final LoanRepository loanRepo;
private final OutboxRepository outboxRepo;
public LoanService(LoanRepository loanRepo, OutboxRepository outboxRepo) {
this.loanRepo = loanRepo;
this.outboxRepo = outboxRepo;
}
@Transactional
public void initiateLoan(String customerId, String loanType, BigDecimal amount) {
UUID loanId = UUID.randomUUID();
LoanApplication loan = new LoanApplication(loanId, customerId, loanType, amount, "INITIATED");
loanRepo.save(loan);
OutboxEvent event = new OutboxEvent(
UUID.randomUUID(), loanId, "LOAN_INITIATED",
"{\"amount\":" + amount + ",\"loanType\":\"" + loanType + "\"}",
"PENDING"
);
outboxRepo.save(event);
}
}
@Service
public class OutboxPoller {
private final OutboxRepository outboxRepo;
private final KafkaTemplate<String, String> kafkaTemplate;
@Scheduled(fixedRate = 5000) // every 5s
@Transactional
public void publishEvents() {
List<OutboxEvent> pendingEvents = outboxRepo.findByStatus("PENDING");
for (OutboxEvent e : pendingEvents) {
kafkaTemplate.send("loan-status", e.getEventType(), e.getPayload());
e.setStatus("PROCESSED");
outboxRepo.save(e);
}
}
}
application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/loandb
username: loanuser
password: loanpass
jpa:
hibernate:
ddl-auto: update
show-sql: true
kafka:
bootstrap-servers: localhost:9092
✅ With this skeleton:
Start Postgres + Kafka (docker-compose).
Call LoanService.initiateLoan() → Row inserted into loan_application + outbox_event.
Poller picks PENDING → Publishes LOAN_INITIATED → Kafka.
Other services (KYC, Credit, Fraud, etc.) follow same pattern with their own topics (kyc-status, credit-status, etc.).
Comments