FinanceOS MX API
Enterprise-grade REST API for managing personal finances in the Mexican financial ecosystem: accounts, cards, transactions, budgets, loans and investments in a single API.
- Java
- Spring Boot
- PostgreSQL
- Maven
The case study
- Problem
- In Mexico a person’s financial life is scattered across banks, fintechs, SOFIPOs and wallets, each with its own app and format: there is no single place to check what you have, what you owe or what you spent.
- Solution
- A modular monolith in Java 21 and Spring Boot 3 —17 business modules with single-direction dependencies—, stateless JWT security, PostgreSQL with Flyway migrations, RFC 9457 errors and one non-negotiable rule: the balance is never mutated directly, it is always a consequence of the transactions.
- Outcome
- Full roadmap shipped (10 of 10 phases): auth, accounts, cards, transactions, planning, liabilities, investments and reports, with OpenAPI/Swagger, GitHub Actions CI and a final self-audit phase covering architecture, security and test coverage.
Process
Development process
The reasoning behind the technical decisions: why each technology was chosen, what challenges came up and how they were solved.
- 01
Modular monolith, single-direction dependencies
Each business context (account, transaction, budget, investment…) lives in its own module with its own controller, service, repository, DTOs and exceptions; JPA entities are never exposed through the API. Before writing code I documented conventions, security and the data model, and when a validation required breaking dependency direction —like blocking account closure by querying cards— I dropped the validation rather than violate the architecture.
- 02
Money as a domain invariant
Money is always BigDecimal, UUIDs are application-generated, timestamps live in UTC and every schema change is versioned with Flyway. The core rule: an account balance only changes through a single internal mutation point that records the transaction in the same step, preserving the one-way transaction → account dependency. Derived values —budget spent, next billing, returns— are computed per request and never persisted.
- 03
Live verification, not just tests
Every phase closed by running the real packaged JAR against a clean database and verifying with curl, on top of JUnit 5 and Mockito tests. That practice caught the bugs tests were masking —like a LazyInitializationException invisible under transactional tests— and the final phase was a self-audit: forbidden inter-module dependencies, mappers touching lazy relations, duplication and a security review. Architecture rules do not enforce themselves; they must be audited.
Repositories
Commits
- V1
dd562c2Eduardolast week