Date: 2026-03 | Status: Accepted
Context: A structured architecture was needed to reflect the financial business domains.
Options considered:
- Classic layered structure (
controllers/,services/at root level) - Microservices
- Hybrid DDD (layers inside each domain)
Decision: Hybrid DDD
Rationale: The classic layered structure mixes all domains and does not scale well.
Microservices are excessive without orchestration infrastructure.
Hybrid DDD provides domain cohesion (auth/, user/, wallet/, transaction/)
without the operational complexity of microservices.
Date: 2026-03 | Status: Accepted
Context: Spring Boot can auto‑create tables using ddl-auto.
Options considered:
ddl-auto: update— automatic schema updatesddl-auto: create-drop— recreate schema on each startup- Manual SQL scripts +
ddl-auto: validate
Decision: SQL scripts + validate
Rationale: In a financial application, the database is critical and must be auditable. Hibernate DDL auto does not provide control over custom indexes, constraints, ACID triggers, or PostgreSQL functions. SQL scripts are versioned in Git and fully reproducible.
Date: 2026-03 | Status: Accepted
Context: Needed secure authentication for a financial REST API.
Options considered:
- Single JWT (24h)
- Single JWT (15min, no refresh — frequent re‑login)
- Dual‑token JWT: Access (15min) + Refresh (7 days)
- Stateful HTTP sessions
Decision: Dual‑token JWT
Rationale:
- 24h tokens are unacceptable in finance — a stolen token grants nearly a full day of access
- 15min without refresh harms UX
- Dual‑token provides a short exposure window (15min) with smooth UX
- Stateless design scales horizontally without shared sessions
- Refresh Tokens stored in DB allow explicit revocation (real logout)
Consequence: Requires /auth/refresh endpoint and sessions table in DB..
Date: 2026-03 | Status: Accepted
Context: Needed a second authentication factor for sensitive operations.
Options considered:
- SMS OTP
- Email OTP
- TOTP (Google Authenticator / Authy)
- WebAuthn / FIDO2
Decision: TOTP (RFC 6238)
Rationale:
| Method | Issue |
|---|---|
| SMS | Vulnerable to SIM swapping — common fintech attack vector |
| Email OTP | Depends on email security; variable latency |
| WebAuthn | Requires hardware keys — too complex for a portfolio project |
| TOTP | Offline, standard, no third‑party dependency, widely supported |
NIST SP 800‑63B discourages SMS for high‑value authentication since 2016. TOTP is the standard for serious financial applications.
Consequence: Users must configure an authenticator app (Google Authenticator, Authy, etc.).
Date: 2026-03 | Status: Accepted
Context: Determine the amount at which 2FA is required for transfers.
Decision: $100
Rationale: A $5,000 threshold implies that $499 transactions are “not important,” which is incorrect in financial security. Most real‑world user damage occurs in small, frequent transactions, not large one‑off transfers. A $100 threshold protects ~95% of everyday operations without harming UX for low‑risk payments (< $100).
Date: 2026-03 | Status: Accepted
Context: Spring Boot uses Logback by default.
Decision: Log4j2 2.25.3 with exclusion of spring-boot-starter-logging
Rationale:
- High‑performance async logging — critical with heavy audit logging
- Version 2.25.3 fully patched (CVE‑2021‑44228 Log4Shell)
- More granular configuration to separate security logs from application logs
Date: 2026-03 | Status: Accepted (with note)
Context: Evaluate encryption of sensitive data (balances, account numbers).
Options considered:
- Application‑level encryption (Java) before persisting
- Column‑level encryption with PostgreSQL (
pgcrypto) - Disk/volume‑level encryption (infrastructure)
- No additional encryption (only BCrypt for passwords)
Decision: Delegate encryption to infrastructure + BCrypt for passwords
Rationale:
- Encrypting balances at application level breaks arithmetic operations in DB
pgcryptoadds significant operational complexity for queries- For a portfolio project, explicitly delegating encryption is more honest and professional than implementing it poorly
- In real production: encrypted volumes (AWS EBS encrypted, etc.)
pgcryptofor highly sensitive fields (account number, IBAN)
Note: This decision would be revisited before a real production deployment. Documented explicitly to show it was evaluated, not ignored.
Date: 2026-03 | Status: Accepted
Context: Auditing could be implemented as a cross‑cutting concern (AOP) or as its own domain.
Options considered:
- AOP with
@Aroundon service methods - Audit logic inside each service
- Independent
audit/domain withAuditService
Decision: Independent audit/ domain
Rationale: In financial systems, auditing is a regulatory business requirement, not just a technical concern. Having it as a domain makes it visible, testable, and modifiable without side effects on other domains. AOP is convenient but opaque — it becomes unclear what is being audited and why without reading the aspect code.