|
| 1 | +# Sprank |
| 2 | + |
| 3 | +Sprank is a robust, ledger-based banking and payment processing API built with Spring Boot. |
| 4 | + |
| 5 | +This project was engineered to manage a complex, real-world financial domain model, using a **double-entry bookkeeping |
| 6 | +system for transaction integrity and audibility**. It accurately handles a wide array of core banking features, |
| 7 | +including: |
| 8 | + |
| 9 | +* Cash deposits and transfers |
| 10 | +* Real-time transaction processing |
| 11 | +* Taxes and service fees calculations |
| 12 | +* QR payments and payment requests |
| 13 | +* Multiple currencies |
| 14 | + |
| 15 | +**Index** |
| 16 | + |
| 17 | +* [The main purpose of the project](#the-main-purpose-of-the-project) |
| 18 | +* [Screenshots](#screenshots) |
| 19 | +* [Features](#features) |
| 20 | +* [Project structure](#project-structure) |
| 21 | +* [Technical highlights](#technical-highlights) |
| 22 | +* [Limitations](#limitations) |
| 23 | + |
| 24 | +### The main purpose of the project |
| 25 | + |
| 26 | +My goal is to highlight my ability to create complex systems with complex business logic. That's what inspired many |
| 27 | +decisions like the ledger-based system, of course having a `balance` column in the database would've been much easier |
| 28 | +but not as interesting. |
| 29 | + |
| 30 | +### Screenshots |
| 31 | + |
| 32 | +#### Grafana dashboard |
| 33 | + |
| 34 | + |
| 35 | + |
| 36 | +#### Transaction legs in the database |
| 37 | + |
| 38 | +Query: |
| 39 | + |
| 40 | +```sql |
| 41 | +SELECT t.id, t.created_at, tl.id, tl.account_id, running_balance, debit_amount debit, credit_amount credit FROM transaction t JOIN sprank.transaction_leg tl on t.id = tl.transaction_id ORDER BY t.created_at; |
| 42 | +``` |
| 43 | + |
| 44 | +The screenshot below showcases a few deposits and transactions (the ones that were used to demo the Grafana dashboard) |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | +#### Entity-relationship diagram (ERD) |
| 49 | + |
| 50 | + |
| 51 | + |
| 52 | +### Features |
| 53 | + |
| 54 | +The main features implemented right now are: |
| 55 | + |
| 56 | +* Double-entry bookkeeping system for transaction auditability and integrity |
| 57 | +* Alias system for easy to remember accounts (similar to Argentina's alias/CVU/CBU system) |
| 58 | +* Administrative freezing and unfreezing of accounts |
| 59 | +* Multiple currencies |
| 60 | +* Transaction requests, which allow for QR-code based payments |
| 61 | +* Cash deposits with a cash reserve system |
| 62 | +* Configurable taxes and fees that can apply depending on different facts about the transaction, accounts involved, |
| 63 | + customers, etc. |
| 64 | +* Externally managed auth using Keycloak as IdP |
| 65 | +* Fully Dockerized setup (application, DB and metrics) |
| 66 | +* CI/CD GitHub pipeline for continuous builds and tests |
| 67 | +* Business metrics with Micrometer/Prometheus/Grafana |
| 68 | + |
| 69 | +### Project structure |
| 70 | + |
| 71 | +The project follows a simple structure. It is **not** domain separated since it only has a few domains, and is |
| 72 | +implemented |
| 73 | +a bit like how you'd implement a single-domain microservice. |
| 74 | + |
| 75 | +``` |
| 76 | +main |
| 77 | +├── java/dev/maddock/sprank |
| 78 | +│ ├── advice Controller advice (exception handling) |
| 79 | +│ ├── annotation Utility annotations |
| 80 | +│ │ ├── resolver Annotation resolvers (for Spring's MethodArgumentResolvers) |
| 81 | +│ │ └── validator Validators (for annotations used to validate DTOs) |
| 82 | +│ ├── config Config classes |
| 83 | +│ │ ├── fee Fee-related transaction modifiers |
| 84 | +│ │ └── tax Tax-related transaction modifiers |
| 85 | +│ ├── controller HTTP Controllers (@RestController) |
| 86 | +│ │ ├── account |
| 87 | +│ │ ├── customer |
| 88 | +│ │ ├── deposit |
| 89 | +│ │ ├── request |
| 90 | +│ │ └── transaction |
| 91 | +│ ├── dto DTOs |
| 92 | +│ │ ├── account |
| 93 | +│ │ ├── customer |
| 94 | +│ │ ├── deposit |
| 95 | +│ │ ├── error |
| 96 | +│ │ ├── request |
| 97 | +│ │ └── transaction |
| 98 | +│ ├── entity JPA entities |
| 99 | +│ ├── enumeration Enums |
| 100 | +│ ├── exception Business-specific exceptions (like InsufficientFundsException) |
| 101 | +│ │ ├── account |
| 102 | +│ │ ├── auth |
| 103 | +│ │ ├── customer |
| 104 | +│ │ ├── deposit |
| 105 | +│ │ ├── exchange |
| 106 | +│ │ └── transaction |
| 107 | +│ ├── repository JPA repositories |
| 108 | +│ └── service Service interfaces |
| 109 | +│ └── impl Service implementations |
| 110 | +└── resources |
| 111 | + ├── db Database stuff, includes a script with default accounts for transaction modifiers |
| 112 | + │ └── migration Flyway migrations |
| 113 | + └── META-INF |
| 114 | +``` |
| 115 | + |
| 116 | +### Technical highlights |
| 117 | + |
| 118 | +* **Stack:** Spring Boot, MySQL (JPA/Hibernate) |
| 119 | + |
| 120 | +* **Security:** Managed by Keycloak as the dedicated identity provider (IdP), |
| 121 | + see [docker-compose.yml](docker-compose.yml) |
| 122 | + |
| 123 | +* **CI/CD:** Fully containerized with a [Dockerfile](Dockerfile) and [docker-compose.yml](docker-compose.yml) setup. The |
| 124 | + pipeline |
| 125 | + includes a build and a test step and is available |
| 126 | + on [the GitHub Actions page](https://github.com/SowTag/sprank/actions) |
| 127 | + |
| 128 | +* **Testing:** The API is thoroughly tested and has reached 90% test coverage ( |
| 129 | + see [test/](src/test/java/dev/maddock/sprank)). The current test count |
| 130 | + as of commit [62aa1dd](https://github.com/SowTag/sprank/commit/62aa1dda5f995425791723c8c24789a6d15394e9) is **129**, |
| 131 | + covering over **6000 lines of code**. |
| 132 | + |
| 133 | +* **Documentation:** The project is also extensively documented, with Javadoc comments, READMEs and even |
| 134 | + an [OpenAPI Specification](doc/sprank-openapi.yaml). |
| 135 | + |
| 136 | +* **Monitoring:** The project exposes Micrometer metrics over the `/actuator/prometheus` endpoint, allowing monitoring |
| 137 | + of different business and runtime metrics [(screenshot)](./doc/assets/grafana-dash.png) |
| 138 | + |
| 139 | +* **Code quality:** Apart from being extensively documented, the code applies common practices like SOLID and DRY. |
| 140 | + |
| 141 | +### Limitations |
| 142 | + |
| 143 | +There's a small limitation with the current system. Ideally, in a double-entry bookkeeping system, amounts are stored in |
| 144 | +a common currency, used as a reference value (commonly known as the "base" or "functional" currency). Being an |
| 145 | +argentinian |
| 146 | +myself, I'm well accustomed to inflation and volatility, and a conscious choice has been made to record transactions |
| 147 | +using |
| 148 | +**each account's own currency**. This is technically incorrect, but a decent and (in my opinion) acceptable compromise. |
| 149 | + |
| 150 | +Using a single currency would require accounting for asset loss/growth thanks to currency value fluctuations, which |
| 151 | +would |
| 152 | +require handling income and expenses. This requirement also includes calculating said income and expenses, which in turn |
| 153 | +increases complexity considerably. |
| 154 | + |
| 155 | +_TL;DR: I deliberately chose to implement double-entry bookkeeping slightly incorrectly for simplicity and to reduce |
| 156 | +time to portfolio (TTP!)_ |
0 commit comments