Inside `Online_Banking_System`: How a JavaFX Banking App Moves Money Without Framework Magic
A layered desktop app built with Java 21, JavaFX, JDBC, H2, and manual dependency injection, with one transfer flow that does the most important job in banking: keep every debit, credit, and log entry in sync.
- This repo is interesting because it makes banking mechanics legible by showing the transfer boundary instead of hiding it behind framework magic.
- The most important design choice is a single JDBC connection with manual commit and rollback, which keeps balances and transaction logs synchronized.
- Manual dependency injection and embedded schema creation make the app portable and easy to study without a heavyweight container or external setup.
- The project reads like a strong educational reference because it exposes its trade-offs instead of pretending to be a hardened banking system.
The real product is the transaction
Most banking apps want to impress you with dashboards, forms, and a polished login screen. This one earns attention somewhere less glamorous: in the code that decides whether a transfer succeeds without corrupting the ledger. That is the part that matters.
The repo’s core idea is simple. A transfer is not a UI event. It is a controlled sequence of debit, credit, and transaction log writes that either all happen together or do not happen at all.
That is why the project is more interesting than a typical JavaFX demo. The UI is just the surface. The intellectual center is the discipline around money movement.
A layered desktop app on purpose
The codebase is organized the way a serious desktop app should be organized: JavaFX controllers at the top, services in the middle, JDBC repositories below, and infrastructure code handling database access and dependency wiring. It is not layering for ceremony. It is layering so each part can do one job well.
| Layer | Responsibility | Why it matters |
|---|---|---|
| JavaFX controllers | Handle user actions and update views | Keeps UI code thin |
| Services | Orchestrate business logic | Makes transfers and workflows explicit |
| Repositories | Run SQL and map rows | Keeps persistence separate from business rules |
| Infrastructure | Build the data source and object graph | Removes startup and wiring friction |
The contrast with a framework-heavy default is the point. A Spring Boot app would hide some of this behind annotations and auto-configuration. This repository makes the plumbing visible, which is exactly why it is useful for learning.
Manual dependency injection, built to fit JavaFX
Layers: JavaFX UI, services, JDBC repositories (H2 embedded by default), and pooled connections via HikariCP.
The app builds its own object graph. `ApplicationContext` wires repositories, services, and controllers together, then hands JavaFX an object factory through `FXMLLoader`. That avoids the usual no-args-constructor limitation without dragging in a full dependency injection container.
This matters because it keeps the app understandable. You can trace every dependency by reading the code. No magic, no hidden lifecycle.
Why the transfer service matters most
`TransferService.java` is the file that makes the whole architecture worth studying. It manages a single JDBC connection, disables auto-commit, performs the balance updates, records the transaction entries, then commits only after every step succeeds.
That sequence is the banking logic. The app is not just moving numbers around. It is protecting consistency across account balances and the transaction log at the same time.
The rollback path is just as important. If any step fails, the connection backs out the whole operation, which is exactly what you want when the unit of work is money.
This is where the repository becomes more than a CRUD sample. It teaches the core habit of financial software: treat a transfer as an all-or-nothing unit, not a pile of independent updates.
The database starts itself
The app does not ask you to set up a database manually before it can run. `DataSourceFactory` bootstraps the embedded H2 schema on startup, which means the project is portable in a way many desktop apps are not.
That is a small detail with outsized value. A repository you can clone and launch immediately is much easier to evaluate, much easier to teach from, and much harder to break during setup.
The trade-off is that startup code now owns schema creation. For a learning project, that is a reasonable cost because it removes friction from the first run.
Repositories keep SQL and objects apart
public class JdbcAccountRepository implements AccountRepository {
private final DataSource dataSource;
public JdbcAccountRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Optional<Account> findById(long id) {
String sql = "SELECT id, user_id, account_number, balance FROM accounts WHERE id = ?";
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setLong(1, id);
try (ResultSet rs = statement.executeQuery()) {
if (!rs.next()) return Optional.empty();
return Optional.of(mapRow(rs));
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
The repository pattern is doing real work here. SQL stays in one place, the object mapping stays in one place, and the service layer can think in terms of accounts and transfers instead of result sets and column indexes.
| Approach | What you gain | What you give up |
|---|---|---|
| Prepared statements + row mappers | Explicit control and readable SQL | More code than an ORM |
| ORM abstraction | Less boilerplate | Less visibility into the database work |
| SQL in controllers | Fast to prototype | Harder to test and maintain |
| Repository layer | Clear boundaries | Requires a little discipline |
That discipline is the story of the repository. The project refuses to blur business logic into persistence logic, and that keeps it surprisingly easy to reason about.
What this project gets right, and where it stops
Online Banking Desktop (JavaFX) JavaFX desktop client for an online banking system.
The strongest thing about this repository is its clarity. It shows how a desktop banking app can be wired, booted, and kept consistent with plain Java, JDBC, and a small amount of infrastructure code.
The limits are real too. The repository notes plain-text password comparison, which is fine for a prototype but not acceptable for a production banking system. The code also reads like an educational foundation, not a hardened platform with deep test coverage and compliance controls.
| Strength | Limitation |
|---|---|
| Explicit transfer atomicity | Not production-grade security |
| Portable embedded setup | Prototype-level maturity |
| Readable layered architecture | Limited evidence of broad automated testing |
That honesty is a feature. The repo is valuable because it demonstrates the mechanics cleanly and leaves the hardening work visible instead of pretending it is already solved.





