A modular Java banking platform that simulates common retail banking flows such as onboarding customers, managing multi-type accounts, and executing money-movement operations with audit trails. The system prioritizes correctness, extensibility, and an operator-friendly console experience.
- Browse the curated knowledge base in the project wiki for quick-start guides, architecture summaries, operations runbooks, and troubleshooting tips.
- Detailed reference material continues to live under the
docs/directory for deep dives into specific subsystems.
- Customers & Accounts: Create Savings, Current, and Fixed Deposit accounts with configurable rules, interest accrual, and account lifecycle management.
- Transaction Processing: Queue-backed execution of deposits, withdrawals, transfers, and interest postings with transaction history retention.
- Operator Console: Guided CLI backed by
ConsoleUIfor everyday teller workflows, including quick search and reporting utilities. - HTTP API:
ApiApplicationwraps the domain layer in a hardened HTTP adapter that exposes health, metrics, account discovery, account maintenance, and transaction queuing endpoints suitable for automation and integration tests. - Observability: Observer pattern connects
ConsoleNotifierandTransactionLoggerto important account events for traceability. - Persistence & Caching:
AccountRepositorypluggably stores serialized account aggregates. The default in-memory repository is ideal for demos, while the JDBC implementation targets relational databases with migration support.CacheProviderinstances sit in front of the repository to accelerate hot-path reads and can be swapped or disabled via configuration.
The application uses a layered design built around the Bank aggregate:
- Presentation:
ConsoleUIorchestrates user interactions and translates console actions into domain commands. - Domain & Services:
Bank,Accounthierarchy, and concreteAccountOperationimplementations encapsulate business rules and concurrency controls. - Infrastructure:
AccountRepository(withJdbcAccountRepositoryandInMemoryAccountRepository) provides durable storage.MigrationRunnerapplies SQL migrations during startup, and the asynchronous executor coordinates background processing.
Detailed designs and diagrams are available in the System Design and Low-Level Architecture guides. The high-level component relationships are shown below.
graph TD
ConsoleUI -->|issues commands| Bank
ConsoleUI -->|notifies| ConsoleNotifier
Bank -->|persists via| AccountRepository
Bank -->|owns| AccountFactory
Bank -->|manages| Account
Account -->|emits events| TransactionLogger
Account -->|records| BaseTransaction
- Providers:
CacheProviderFactoryselects an implementation based onCACHE_PROVIDER(or JVM propertybanking.cache.provider). The defaultmemoryoption enables the in-processInMemoryCacheProvider, whilenoneswitches to the no-op adapter for troubleshooting or environments that prefer cold reads. - Scope: The
Bankservice uses caches for fullAccountsnapshots and derived balances, warming them on startup and keeping entries coherent after every successful mutation. - TTLs: Entries expire after five minutes unless overridden with
CACHE_TTL_SECONDS. Specify per-cache TTLs withCACHE_ACCOUNT_TTL_SECONDSandCACHE_BALANCE_TTL_SECONDS. Setting a TTL to zero or a negative value disables expiration for that cache.
Need a step-by-step workstation guide? Follow the Windows 11 setup instructions for tooling installation, environment variables, and local bootstrap commands.
- Install JDK 17 and clone the repository.
- Compile the sources from the project root:
mkdir -p build/classes find src -name '*.java' > sources.txt javac -d build/classes @sources.txt
- Export the database connection (replace the credentials/host with your environment):
export BANKING_STORAGE_MODE=jdbc
export BANKING_JDBC_URL="jdbc:mysql://localhost:3306/banking?useSSL=true&requireSSL=false&serverTimezone=UTC"
export BANKING_DB_USER="bank_user"
export BANKING_DB_PASSWORD="ChangeMe123!"
# Optional cache tuning
export CACHE_PROVIDER=memory # or "none" to disable caching entirely
export CACHE_TTL_SECONDS=600 # override per-cache TTL with CACHE_ACCOUNT_TTL_SECONDS / CACHE_BALANCE_TTL_SECONDS- Run database migrations:
bash deploy/scripts/run-migrations.sh
- Launch the console application:
java -cp build/classes banking.BankingApplication
- Confirm the startup banner and ensure accounts persist across restarts by relaunching the console or API.
- Ensure the classes are compiled and the environment variables from the previous section are still exported.
- Start the API server:
java -cp build/classes banking.api.ApiApplication
- In a separate terminal, request an operator token and exercise the API (replace
<ACCOUNT_NUMBER>with the value returned by the creation call). Every request must include the bearer token header:TOKEN=$(curl -s -X POST "http://localhost:8080/auth/login" -d "username=admin&password=admin123!" | jq -r .token) curl -H "Authorization: Bearer $TOKEN" -X POST "http://localhost:8080/accounts" -d "name=Grace&type=savings&deposit=1000" curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/accounts" curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/accounts/<ACCOUNT_NUMBER>" curl -H "Authorization: Bearer $TOKEN" -X PUT "http://localhost:8080/accounts/<ACCOUNT_NUMBER>" -d "userName=Grace%20Hopper" curl -H "Authorization: Bearer $TOKEN" -X DELETE "http://localhost:8080/accounts/<ACCOUNT_NUMBER>"
- Inspect
/metricswith the bearer token header to verify uptime, account counts, and queue depth telemetry:curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/metrics"
- Stop the process with
Ctrl+C; graceful shutdown ensures all in-flight operations complete and state is flushed viaBankDAO.saveBank.
- Backup: Schedule nightly logical dumps (
mysqldump banking > banking-$(date +%F).sql) or point-in-time backups from your managed MySQL service. Optionally archivebanking_state.propertieswhen running in snapshot mode for local experiments. - Restore: Apply database dumps to a recovery environment and point the application’s
BANKING_JDBC_URLto the restored cluster. Legacy serialized.serfiles are automatically converted into snapshots on first boot and subsequently migrated into MySQL. - Reset: Drop and recreate the schema for a clean slate (
DROP DATABASE banking; CREATE DATABASE banking;).
- Database Connectivity: Validate
BANKING_JDBC_URL, username, and password.mysql -h <host> -u <user> -pshould succeed from the host where the API runs. - Schema Drift: Re-run
bash deploy/scripts/run-migrations.shafter promoting new releases so the schema version matches the application expectations. - Authentication Errors: Ensure every HTTP request includes the
Authorization: Bearer <token>header. Issue a new token from the console or/auth/loginendpoint if the current token expires. - Snapshot Mode Testing: When experimenting with the filesystem repository locally, unset
BANKING_STORAGE_MODEand ensureBANKING_DATA_PATHpoints to a writable location.
- Docker Compose:
docker compose -f deploy/compose/docker-compose.yml upbuilds the console/API images and provisions a local MySQL container wired with matching environment variables for parity with staging. Tear down withdocker compose ... downwhen finished. - Terraform Deployments: Export
TF_VAR_db_password(or inject via your secrets manager) before applyingdeploy/terraform/environments/<env>.tfvars. The module creates Kubernetes secrets, deployments, and autoscaling policies that expect a managed MySQL endpoint defined byjdbc_url.
- Observability Enhancements: Ship structured logs to a centralized aggregator and publish RED metrics for key flows.
- Security Hardening: Layer on mTLS between services, add request signing for console automations, and integrate with an identity provider for operator authentication.
- Domain Testing: Expand the automated test suite with property-based tests and golden-file regression checks for reporting outputs.
- Performance: Introduce connection pooling and bulk operation APIs for batched ingest workloads.
- Disaster Recovery Drills: Automate failover simulations across regions and document recovery point objectives empirically.
- Install JDK 11+ and clone the repository.
- Compile from the project root:
javac $(find src/main/java -name "*.java"). - Start the console application:
java -cp src/main/java banking.BankingApplication. - By default the in-memory repository is used. To exercise the JDBC path, set environment variables such as
BANKING_PERSISTENCE=jdbcandBANKING_JDBC_URL=jdbc:h2:mem:bank;DB_CLOSE_DELAY=-1before launching. - When you are done for the day, exit through menu option 7 so outstanding asynchronous operations flush and the repository closes cleanly.
- Backup: For JDBC deployments, follow your database's backup tooling (e.g.,
pg_dump,mysqldump, H2 file copy). For in-memory demos, no backup is required. - Restore: Restore the relational database snapshot before launching. The migration runner will detect existing schema versions and skip reapplied scripts.
- Reset: Drop the
accountstable (or use a new JDBC URL) to clear persisted data. The in-memory repository always starts empty.
- Database Connectivity: Verify JDBC URLs, credentials, and drivers (
BANKING_JDBC_DRIVER) when the application cannot connect during startup. - Stalled Operations: Ensure the executor thread pool is not exhausted; restart the app to reinitialize the queue.
- Invalid Inputs: Watch console prompts—validation errors indicate the value that needs correction.
- API Gateway: Expose REST endpoints with Spring Boot for web/mobile clients.
- Observability Enhancements: Wire the repository to metrics/logging to monitor query latency and failure rates.
- Authentication: Add role-based access control with audit logging.
- Reporting Suite: Generate configurable statements and regulatory reports.
- CI/CD Automation: Introduce automated builds, tests, and packaging pipelines.
Community guidelines live in CONTRIBUTING.md and CODE_OF_CONDUCT.md. Start there before submitting issues or pull requests.