A junior portfolio project demonstrating a Spring Boot REST API for a simple banking application.
The API provides account management and financial transaction capabilities backed by MySQL, with transaction management, optimistic locking, request validation, consistent HTTP error responses, database migrations, automated integration testing, and OpenAPI documentation.
The Enterprise Banking API is the backend service for a full-stack banking application.
It currently provides REST endpoints for:
- Retrieving account information
- Retrieving transaction history
- Depositing funds
- Withdrawing funds
- Transferring funds between accounts
Financial operations use Spring transaction management so related database changes succeed or roll back together.
- Account balance retrieval
- Transaction history
- Deposits
- Withdrawals
- Account-to-account transfers
- Atomic transfer processing
- Transaction rollback for failed operations
- Request validation
- Global exception handling
- RFC 9457-style
ProblemDetailerror responses - Optimistic locking for concurrent account updates
- MySQL persistence
- Flyway database migrations
- H2-based automated tests
- OpenAPI / Swagger documentation
- CORS configuration through environment variables
- Maven-based build and test lifecycle
| Technology | Purpose |
|---|---|
| Java 25 | Application language and runtime |
| Spring Boot 4.1.1 | Application framework |
| Spring MVC | REST API |
| Spring Data JPA | Persistence abstraction |
| Hibernate | ORM |
| MySQL | Application database |
| Flyway | Database migrations |
| H2 | Test database |
| Maven | Build and dependency management |
| SpringDoc OpenAPI | API documentation |
| JUnit | Automated testing |
The application follows a layered architecture:
HTTP Request
|
v
Controller
|
v
Service
|
v
Repository
|
v
MySQL Database
Handles HTTP requests, request validation, API documentation, and HTTP responses.
Contains banking business logic and transaction boundaries.
Provides persistence through Spring Data JPA.
The API maps account and transaction records to MySQL tables. Accounts include a customer ID value and an optimistic-locking version column; there is no separate Customer entity.
The current schema has two tables. The diagram lists representative columns; customer_id and transactions.account_number are values in the current migration, not declared foreign keys.
erDiagram
accounts {
INT account_number PK
INT customer_id
VARCHAR account_type
DECIMAL balance
BIGINT version
VARCHAR pin
INT failed_attempts
BIT is_locked
}
transactions {
INT transaction_id PK
INT account_number
VARCHAR transaction_type
DECIMAL amount
DECIMAL balance_after
DATETIME transaction_date
}
enterprise-banking-api/
│
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/phakiso/enterprisebankingapi/
│ │ │ ├── account/
│ │ │ ├── config/
│ │ │ ├── exception/
│ │ │ └── transaction/
│ │ │
│ │ └── resources/
│ │ ├── application.yaml
│ │ └── db/
│ │ └── migration/
│ │
│ └── test/
│ ├── java/
│ │ └── com/phakiso/enterprisebankingapi/
│ └── resources/
│ └── application-test.yaml
│
├── pom.xml
├── mvnw
├── mvnw.cmd
├── .gitignore
└── README.md
Base URL:
http://localhost:8080
Retrieve account details:
GET /api/v1/accounts/{accountNumber}Example:
GET /api/v1/accounts/888888Example response:
{
"accountNumber": 888888,
"customerId": 888888,
"accountType": "Savings",
"balance": 49000.00
}POST /api/v1/accounts/{accountNumber}/depositsExample request:
{
"amount": 1000.00
}POST /api/v1/accounts/{accountNumber}/withdrawalsExample request:
{
"amount": 500.00
}POST /api/v1/accounts/{accountNumber}/transfersExample request:
{
"destinationAccountNumber": 999999,
"amount": 1000.00
}Transfers are processed atomically. The source account, destination account, and transaction records are handled within the same transaction boundary.
GET /api/v1/accounts/{accountNumber}/transactionsReturns transaction history associated with an account.
The API uses a centralized exception-handling mechanism.
Errors are returned using Spring's ProblemDetail representation where appropriate.
Typical HTTP responses include:
| Status | Meaning |
|---|---|
200 |
Operation completed successfully |
400 |
Invalid request or validation failure |
404 |
Account or resource does not exist |
409 |
Concurrent account modification |
422 |
Business rule violation such as insufficient funds |
Account updates use optimistic locking to protect against concurrent modifications.
The account entity maintains a version value which allows the application to detect when another transaction has modified the same account before an update is completed.
A concurrent modification results in an appropriate conflict response rather than silently overwriting another transaction's changes.
The application uses MySQL.
Database schema changes are managed through Flyway migrations located under:
src/main/resources/db/migration/
Migrations run in order on a new empty database:
V1__create_account_tables.sql
V2__add_account_version.sql
V1 creates the accounts and transactions tables. V2 adds the account version used for optimistic locking. The existing baseline-on-migrate setting supports databases whose tables were created before Flyway history was introduced. Automated tests use an isolated H2 schema created by Hibernate and do not run Flyway migrations.
Database credentials are supplied through environment variables. CORS allows one configured frontend origin, and Actuator exposes only health information. The API does not implement login or authorization; authentication is intentionally outside this junior portfolio project's scope. This junior portfolio project is not a production banking service.
Application configuration is located in:
src/main/resources/application.yaml
Test-specific configuration is located in:
src/test/resources/application-test.yaml
Environment-specific values should be supplied through environment variables rather than committed credentials:
| Variable | Required | Default | Purpose |
|---|---|---|---|
| DB_PASSWORD | Yes for a password-protected MySQL account | None | MySQL account password |
| DB_URL | No | jdbc:mysql://localhost:3306/enterprise_banking | JDBC connection URL |
| DB_USERNAME | No | root | MySQL account name |
| APP_CORS_ALLOWED_ORIGIN | No | http://localhost:5173 | Allowed frontend origin |
Never put a real password in source control or shell history. Set DB_PASSWORD in your local environment or secret manager. The .gitignore excludes local .env files; .env.example may contain placeholders only.
The API includes OpenAPI documentation through SpringDoc.
When the application is running, Swagger UI is available at:
http://localhost:8080/swagger-ui.html
The generated OpenAPI specification is available at:
http://localhost:8080/v3/api-docs
These endpoints can be used to explore and test the API interactively.
- Java 25
- MySQL
- The included Maven wrapper (a separate Maven installation is not required)
Create the database before starting the application. In your MySQL client, run:
CREATE DATABASE enterprise_banking;
Set DB_PASSWORD in your local environment to the MySQL account password. The default connection uses root on localhost; set DB_USERNAME and/or DB_URL if your setup differs. Use placeholders only in examples; never commit a real password.
From the enterprise-banking-api directory:
.\mvnw.cmd spring-boot:runThe API will start on:
http://localhost:8080
Flyway creates the schema but does not add demo accounts. After the first successful startup, add local sample records if you want to try the account endpoints and frontend:
INSERT INTO accounts (account_number, customer_id, account_type, balance)
VALUES
(888888, 888888, 'Cheque', 31000.00),
(999999, 999999, 'Savings', 2000.00);Automated tests use the test profile's in-memory H2 database. Hibernate creates and drops that isolated schema; Flyway is disabled in this profile. These tests are separate from the completed manual MySQL verification below.
From the enterprise-banking-api directory, run:
.\mvnw.cmd test
This runs the automated test phase. Run the full Maven verification lifecycle, including tests and package checks, with:
.\mvnw.cmd verify
Both commands should finish with BUILD SUCCESS.
Phase 8/9 manual verification is complete and accepted. The recorded evidence covers a completely fresh MySQL database, Spring Boot startup, Flyway V1 then V2, JPA schema validation, successful application startup, and /actuator/health returning HTTP 200. This is separate from the automated H2 test profile and is not being repeated.
With the application running, account retrieval can be verified with:
Invoke-WebRequest http://localhost:8080/api/v1/accounts/888888 -UseBasicParsing |
Select-Object StatusCode, ContentExpected result:
StatusCode: 200
Swagger UI can be verified with:
Invoke-WebRequest http://localhost:8080/swagger-ui.html -UseBasicParsing |
Select-Object StatusCodeOpenAPI can be verified with:
Invoke-WebRequest http://localhost:8080/v3/api-docs -UseBasicParsing |
Select-Object StatusCodeBoth should return:
200
The project contains several layers of automated testing:
Business logic is tested independently of the HTTP layer.
REST controller behaviour, request handling, validation, and responses are tested.
The application context and real HTTP behaviour are tested through isolated integration tests.
Concurrent account modification scenarios are tested to verify conflict handling.
The generated OpenAPI documentation is verified as part of the automated test suite.
The project has evolved incrementally from a basic account API into a junior full-stack portfolio backend.
Major milestones include:
- Account persistence
- Account retrieval API
- Transaction persistence
- Transaction history
- Deposits
- Withdrawals
- Transaction rollback
- API validation
- Global exception handling
- Optimistic locking
- Transfer processing
- Integration testing
- OpenAPI documentation
- Externalized CORS configuration
The backend currently provides:
- RESTful account operations
- Transaction history
- Deposits
- Withdrawals
- Account transfers
- Transaction management
- Optimistic locking
- MySQL persistence
- Flyway migrations
- Global error handling
- OpenAPI documentation
- Automated testing
- Externalized CORS configuration
The API is being developed as the backend foundation for the Enterprise Banking full-stack application.
Frontend: Enterprise Banking Web
The frontend is built with React and Vite and consumes this REST API.
Phakiso
Enterprise Banking API — full-stack banking application portfolio project.