Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ build/
.tmp/
.env
.env.*
target/
.idea/
*.iml
3 changes: 3 additions & 0 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
140 changes: 115 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,125 @@
# Wallet Transfer Assignment Repository
# Wallet Transfer Service

This repository is a reusable coding assignment template for evaluating backend engineers on wallet transfers, idempotency, concurrency control, and double-entry ledger design.
A robust, transactional backend service built using **Spring Boot 3.x** and **Java 21** that supports wallet-to-wallet transfers.

## Included
This implementation prioritizes exactly-once API semantics (idempotency), database consistency (double-entry ledger), and safe concurrent execution (deadlock prevention & locking).

- `ASSIGNMENT.md` - candidate-facing prompt
- `.github/pull_request_template.md` - required PR structure
- `.github/workflows/ci.yml` - lint, format, test placeholder workflow
- `.github/workflows/sonarqube.yml` - SonarQube pull request analysis
- `.github/copilot-instructions.md` - repository-level Copilot review guidance
- `evaluation_guide.md` - reviewer rubric
- `branch-protection-checklist.md` - GitHub setup checklist
---

## Intended use
## 🛠️ Technology Stack
- **Language**: Java 21
- **Framework**: Spring Boot 3.x, Spring Data JPA
- **Database**: H2 Database (configured in PostgreSQL compatibility mode for zero-setup execution)
- **Build System**: Maven (wrapper included)

1. Mark this repository as a GitHub template repository.
2. Create one private repository per candidate from the template.
3. Add the candidate as a collaborator.
4. Ask them to submit via a pull request into `main`.
5. Enable required checks, SonarQube, and Copilot review in GitHub.
---

## Notes
## 📐 Architecture & Design Decisions

- Copilot automatic pull request review is configured in GitHub repository or organization settings, not purely through files in the repo.
- The `copilot-instructions.md` file included here provides repository-specific review guidance once Copilot review is enabled.
- The CI workflow is language-agnostic by default and expects you to set the `LINT_CMD`, `FORMAT_CHECK_CMD`, and `TEST_CMD` repository variables or replace the commands directly.
### 1. Database Schema
The database uses H2 (in-memory) with schemas created automatically on startup. The schema includes four main tables:
- **`wallets`**: Contains the account balance with a database constraint (`balance >= 0`) ensuring no wallet can ever drop below zero.
- **`transfers`**: Records each transfer request, its state (`PENDING`, `PROCESSED`, `FAILED`), and any error messages.
- **`ledger_entries`**: A double-entry ledger recording all debits and credits. Every transfer generates exactly two entries (one debit and one credit) to ensure the system balances.
- **`idempotency_records`**: Stores API response bodies and HTTP status codes mapped to client-provided idempotency keys.

## How to Submit Assignment
### 2. Concurrency & Deadlock Prevention
To handle concurrent requests safely without race conditions or double spending:
- **Pessimistic Write Locking**: We acquire a database-level lock (`SELECT ... FOR UPDATE` via JPA `@Lock(LockModeType.PESSIMISTIC_WRITE)`) on the source and destination wallets before validating balances and executing the transfer.
- **Deadlock Avoidance**: When locking two wallets, we sort their IDs lexicographically. We always lock the wallet with the smaller ID first, then the larger ID. This prevents circular dependencies (deadlocks) when concurrent transactions attempt transfers between the same accounts in opposite directions.

1. **Fork this repository** to your own GitHub account.
2. Complete the assignment described in [`ASSIGNMENT.md`](./ASSIGNMENT.md).
3. **Raise a Pull Request** back to this repository (`main` branch) with your full solution.
### 3. Idempotency (Exactly-Once Semantics)
- When a request is received, the service checks `idempotency_records` for the key.
- If it exists, the cached response is instantly returned.
- If it doesn't exist, the transaction is executed. The database enforces a `UNIQUE` constraint on the idempotency key. Any concurrent duplicate request trying to execute at the same microsecond will fail the DB insert, throwing a unique constraint violation which we catch and resolve by returning the correct processed result.

Your PR branch should be named: `solution/<your-name>` (e.g., `solution/jane-doe`).
---

## 🚀 Running the Application

### Prerequisites
- **Java 21** installed on your system.

### Steps to Run
1. Navigate to the project root directory.
2. Build and run the server:
```bash
.\mvnw.cmd spring-boot:run
```
3. Once started, the server listens on port `8080`.

---

## 🧪 Testing

We have built a thorough test suite covering edge cases, state transitions, validation, and multi-threaded concurrency.

To execute the test suite:
```bash
.\mvnw.cmd clean test
```

### Tests Overview:
- **`WalletApplicationTests`**: Verifies application context loading.
- **`TransferConcurrencyTest`**: Spawns multiple concurrent threads attempting to transfer money from the same source wallet simultaneously to verify thread-safety and prevent double spending.
- **`TransferControllerTest`**: Asserts the REST endpoints, validation errors, non-existent wallet handling, and idempotency replay behavior.

---

## 📬 API Specification

### Create Transfer
- **Endpoint**: `POST /transfers`
- **Headers**: `Content-Type: application/json`
- **Request Body**:
```json
{
"idempotencyKey": "unique-uuid-key",
"fromWalletId": "wallet_1",
"toWalletId": "wallet_2",
"amount": 150.00
}
```

- **Successful Response (201 Created)**:
```json
{
"transferId": "48b625ca-d84d-4523-a55e-990c0ef48e24",
"fromWalletId": "wallet_1",
"toWalletId": "wallet_2",
"amount": 150.00,
"status": "PROCESSED",
"errorMessage": null
}
```

- **Insufficient Funds Response (422 Unprocessable Entity)**:
```json
{
"transferId": "9127814b-22fb-4e1b-b27a-85d8d1e21b24",
"fromWalletId": "wallet_1",
"toWalletId": "wallet_2",
"amount": 5000.00,
"status": "FAILED",
"errorMessage": "insufficient funds"
}
```

---

## 🖥️ Database Verification (H2 Console)
While the application is running, you can inspect the database directly:
- **Console URL**: [http://localhost:8080/h2-console](http://localhost:8080/h2-console)
- **JDBC URL**: `jdbc:h2:mem:walletdb`
- **Username**: `sa`
- **Password**: *(leave blank)*

---

## 🤖 AI Usage Disclosure

As requested by the assignment guidelines:

1. **Tool Used**: Antigravity (Advanced Agentic Coding assistant developed by Google DeepMind).
2. **Usage Strategy**: Antigravity was used as a senior developer pair-programmer. It assisted in analyzing the initial workspace, creating the architecture and design plan, verifying compile success and Maven wrapper commands, and structuring the end-to-end execution guide.
3. **Session Transcript**: The full interaction history and prompts are maintained locally in the system generated logs, and a walkthrough log is provided in the repository under [walkthrough.md](./walkthrough.md).
17 changes: 17 additions & 0 deletions angular-frontend/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false

[*.md]
max_line_length = off
trim_trailing_whitespace = false
44 changes: 44 additions & 0 deletions angular-frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.

# Compiled output
/dist
/tmp
/out-tsc
/bazel-out

# Node
/node_modules
npm-debug.log
yarn-error.log

# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/mcp.json
.history/*

# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
__screenshots__/

# System files
.DS_Store
Thumbs.db
12 changes: 12 additions & 0 deletions angular-frontend/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"printWidth": 100,
"singleQuote": true,
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular"
}
}
]
}
4 changes: 4 additions & 0 deletions angular-frontend/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}
20 changes: 20 additions & 0 deletions angular-frontend/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}
42 changes: 42 additions & 0 deletions angular-frontend/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "Changes detected"
},
"endsPattern": {
"regexp": "bundle generation (complete|failed)"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "Changes detected"
},
"endsPattern": {
"regexp": "bundle generation (complete|failed)"
}
}
}
}
]
}
59 changes: 59 additions & 0 deletions angular-frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# AngularFrontend

This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 22.0.3.

## Development server

To start a local development server, run:

```bash
ng serve
```

Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.

## Code scaffolding

Angular CLI includes powerful code scaffolding tools. To generate a new component, run:

```bash
ng generate component component-name
```

For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:

```bash
ng generate --help
```

## Building

To build the project run:

```bash
ng build
```

This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.

## Running unit tests

To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:

```bash
ng test
```

## Running end-to-end tests

For end-to-end (e2e) testing, run:

```bash
ng e2e
```

Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.

## Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
Loading