Skip to content

Va5k3/TalosFinance

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TalosFin — Financial Advisor API

A Spring Boot REST API that assesses whether a client can afford a home or car loan, based on their financial data (income, expenses), using explicit, configurable business rules (DTI threshold, interest rate, inflation factor)

Project idea

A client submits their financial data (salary, additional income, monthly expenses and liabilities) and requests an assessment of whether they can afford a home or car loan, and under what conditions. The system calculates the result based on clear rules instead of a black box — every decision (APPROVED / REJECTED / APPROVED_WITH_NOTE) follows concrete, explainable steps.

Tech stack

  • Java 21
  • Spring Boot 4.1 (Web, Data JPA, Validation)
  • Hibernate ORM
  • PostgreSQL 16 (Docker)
  • Lombok
  • Bean Validation (Jakarta Validation)
  • JUnit 5 + Mockito
  • Maven

Project structure

com.example.TalosFin
├── entity/
│   ├── Klijent.java                (Client)
│   ├── FinansijskaStavka.java      (FinancialItem)
│   ├── TipStavke.java              (enum: PRIHOD/INCOME, TROSAK/EXPENSE)
│   ├── ZahtevZaKredit.java         (LoanRequest)
│   ├── TipKredita.java             (enum: STAN/HOME, AUTO/CAR)
│   ├── Preporuka.java              (Recommendation)
│   ├── StatusPreporuke.java        (enum: APPROVED, REJECTED, APPROVED_WITH_NOTE)
│   └── ParametarSistema.java       (SystemParameter)
├── repository/
│   ├── KlijentRepository.java
│   ├── FinansijskaStavkaRepository.java
│   ├── ZahtevZaKreditRepository.java
│   ├── PreporukaRepository.java
│   └── ParametarSistemaRepository.java
├── service/
│   ├── KlijentService.java
│   ├── ZahtevZaKreditService.java
│   ├── ParametarSistemaService.java
│   └── KalkulacijaService.java      (the decision engine — core business logic)
├── controller/
│   ├── KlijentController.java
│   ├── ZahtevZaKreditController.java
│   └── ParametarSistemaController.java
├── dto/
│   ├── KlijentDTO.java / KreirajKlijentDTO.java
│   ├── FinansijskaStavkaDTO.java
│   ├── ZahtevZaKreditDTO.java / KreirajZahtevDTO.java
│   └── PreporukaDTO.java
├── exception/
│   ├── KlijentNotFoundException.java
│   ├── ZahtevNotFoundException.java
│   ├── ParametarNotFoundException.java
│   ├── NevalidanZahtevException.java
│   └── GlobalExceptionHandler.java
└── TalosFinApplication.java

Klijent = Client, ZahtevZaKredit = LoanRequest, Preporuka = Recommendation

Entities and relationships

Client (1) ──── (N) FinancialItem      [salary, additional income, monthly expenses]
Client (1) ──── (N) LoanRequest         [request: type (HOME/CAR), amount, term]
LoanRequest (1) ──── (1) Recommendation [result: status, max installment, explanation]
SystemParameter (standalone table)      [interest rate, max % of income, inflation]

Design rationale:

  • FinancialItem is a separate entity (not a field on Client) because a client can realistically have multiple income sources and multiple ongoing liabilities (other loan installments, rent...).
  • LoanRequest → Recommendation is a 1:1 relationship — every request gets exactly one recommendation, which also stores a historical snapshot of the parameters (e.g. the interest rate) used at calculation time, even if system parameters change later.
  • SystemParameter is a separate table, not hardcoded values in the code — business rules (interest rate, DTI threshold) can be changed without redeploying the application.

Business logic (KalkulacijaService)

The core of the system — how a loan decision is made:

  1. Calculate the client's total monthly income (sum of FinancialItem of type INCOME)
  2. Calculate total monthly liabilities (sum of type EXPENSE)
  3. Read maxIncomePercentageForInstallment from SystemParameter (e.g. 40% — standard DTI)
  4. Adjust that percentage based on current inflation (higher inflation → more conservative threshold)
  5. Calculate the maximum installment the client can afford = income × adjusted% − liabilities
  6. Calculate the actual installment for the requested loan using the standard annuity formula (amount, interest rate, repayment term)
  7. Compare and return a Recommendation: APPROVED / REJECTED / APPROVED_WITH_NOTE (borderline case), with a text explanation

All monetary values use BigDecimal (never double/float), since binary rounding introduces errors in financial calculations.

Endpoints

Method Path Description
POST /api/klijenti Create a client
GET /api/klijenti/{id} Get client details
GET /api/klijenti List all clients
POST /api/klijenti/{id}/stavke Add an income/expense item
GET /api/klijenti/{id}/stavke List all financial items for a client
POST /api/klijenti/{id}/zahtevi Submit a loan request
GET /api/zahtevi/{id} Get request details + recommendation
GET /api/klijenti/{id}/zahtevi Get a client's request history
GET /api/parametri Get current system parameters
PUT /api/parametri/{naziv} Update a parameter (e.g. interest rate)

Running the project

Prerequisites

  • Java 21 (JDK)
  • Maven (or use the included ./mvnw wrapper)
  • Docker + Docker Compose

Steps

# 1. Start the PostgreSQL database in Docker
docker compose up -d

# 2. Run the application
./mvnw spring-boot:run

The application is available at http://localhost:8080.

On first startup, Hibernate automatically generates the database schema (spring.jpa.hibernate.ddl-auto=update), and data.sql inserts the initial system parameters (interest rate, DTI threshold, inflation) if they don't already exist.

Verifying the database

docker exec -it TalosFin-db psql -U user -d TalosFin -c '\dt'

Usage example (curl)

# Create a client
curl -X POST http://localhost:8080/api/klijenti \
  -H "Content-Type: application/json" \
  -d '{"ime":"Marko","prezime":"Marković","email":"marko@example.com"}'

# Add income (replace {id} with the id from the previous response)
curl -X POST http://localhost:8080/api/klijenti/1/stavke \
  -H "Content-Type: application/json" \
  -d '{"tip":"PRIHOD","naziv":"Salary","iznos":2000}'

# Submit a loan request
curl -X POST http://localhost:8080/api/klijenti/1/zahtevi \
  -H "Content-Type: application/json" \
  -d '{"tipKredita":"STAN","traženiIznos":10000,"rokOtplateMeseci":120}'

The last call returns the full ZahtevZaKreditDTO with a nested preporuka section — status, calculated installment, maximum installment, and a text explanation.

Tests

./mvnw clean test

The focus is on KalkulacijaServiceTest — unit tests for the core business logic (annuity formula, DTI check, inflation impact), isolated from the database using Mockito mocks.

Validation and error handling

  • Bean Validation (@NotBlank, @Email, @Positive, @Min) on input DTOs
  • GlobalExceptionHandler (@RestControllerAdvice) centrally catches all errors (missing resources → 404, invalid requests → 400) and returns a readable JSON response instead of a raw stack trace

About

Spring Boot REST API that assesses home/car loan affordability using explicit, configurable business rules (DTI threshold, interest rate, inflation) — Java, PostgreSQL, Hibernate/JPA

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages