Add enums for LedgerEntryType and TransferStatus; create initial appl…#103
Open
sanchaymehrotra wants to merge 1 commit into
Open
Add enums for LedgerEntryType and TransferStatus; create initial appl…#103sanchaymehrotra wants to merge 1 commit into
sanchaymehrotra wants to merge 1 commit into
Conversation
…ication configuration files
There was a problem hiding this comment.
Pull request overview
Implements a Spring Boot–based wallet transfer service with idempotent transfer processing, pessimistic wallet locking for concurrency safety, and double-entry ledger recording, plus integration tests and supporting project configuration (CI, Sonar, Postman, Swagger).
Changes:
- Added core domain model (wallets/transfers/ledger/idempotency), repositories, services, controllers, and Swagger/OpenAPI config.
- Implemented idempotency via request hashing + persisted cached responses; added concurrency/behavioral integration tests.
- Added runtime/test configuration (profiles, H2/Postgres), CI workflow updates, and supporting docs/artifacts (README, Postman, Sonar, JaCoCo).
Reviewed changes
Copilot reviewed 52 out of 54 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/test/resources/application-test.yml | Test profile H2 + JPA configuration |
| src/test/java/com/wallettransfer/utility/ResponseBuilderFactoryTest.java | Unit tests for response builder behavior |
| src/test/java/com/wallettransfer/utility/LogSanitizerTest.java | Unit tests for log sanitization |
| src/test/java/com/wallettransfer/TransferIntegrationTest.java | Behavioral integration tests incl. idempotency + concurrency |
| src/main/resources/data.sql | Seed wallets for local runs |
| src/main/resources/application.yml | Base Spring Boot configuration (profiles, JPA, management, springdoc) |
| src/main/resources/application-production.yml | Production profile overrides |
| src/main/resources/application-postgres.yml | Postgres profile datasource + JPA config |
| src/main/resources/application-local.yml | Local H2 profile datasource + console + JPA config |
| src/main/java/com/wallettransfer/WalletTransferServiceApplication.java | Spring Boot application entrypoint |
| src/main/java/com/wallettransfer/utility/TransferMapper.java | Entity → response DTO mapping |
| src/main/java/com/wallettransfer/utility/ServiceResponseSupport.java | Wrapper for consistent service exception-to-response behavior |
| src/main/java/com/wallettransfer/utility/RequestHashUtility.java | Request hashing + JSON (de)serialization utilities |
| src/main/java/com/wallettransfer/utility/LogSanitizer.java | Sanitizes log message content |
| src/main/java/com/wallettransfer/service/walletservice/WalletService.java | Balance + history read APIs |
| src/main/java/com/wallettransfer/service/transferservice/TransferService.java | Transfer API orchestration + idempotency handling entrypoint |
| src/main/java/com/wallettransfer/service/transferservice/TransferExecutor.java | Transactional transfer execution (locking, balance updates, ledger, idempotency persistence) |
| src/main/java/com/wallettransfer/service/transferservice/IdempotencyResponseLookup.java | Cached-response and existing-transfer replay lookup |
| src/main/java/com/wallettransfer/service/loggerservice/LogServiceImpl.java | Logging implementation |
| src/main/java/com/wallettransfer/service/loggerservice/LogService.java | Logging interface |
| src/main/java/com/wallettransfer/repo/WalletRepository.java | Wallet persistence + pessimistic lock queries |
| src/main/java/com/wallettransfer/repo/TransferRepository.java | Transfer persistence + idempotency lookup |
| src/main/java/com/wallettransfer/repo/LedgerEntryRepository.java | Ledger entry persistence + history queries |
| src/main/java/com/wallettransfer/repo/IdempotencyRecordRepository.java | Idempotency record persistence + key lookup |
| src/main/java/com/wallettransfer/exception/ResponseException.java | Domain exception carrying response code + optional payload |
| src/main/java/com/wallettransfer/enums/TransferStatus.java | Transfer status enum |
| src/main/java/com/wallettransfer/enums/LedgerEntryType.java | Ledger entry type enum |
| src/main/java/com/wallettransfer/entity/Wallet.java | Wallet entity (balance/version/timestamps + constraint) |
| src/main/java/com/wallettransfer/entity/Transfer.java | Transfer entity (status machine, constraints, indexes) |
| src/main/java/com/wallettransfer/entity/LedgerEntry.java | Ledger entry entity (double-entry record + constraint/indexes) |
| src/main/java/com/wallettransfer/entity/IdempotencyRecord.java | Cached response record entity |
| src/main/java/com/wallettransfer/dto/responsedto/WalletBalanceResponseDTO.java | Balance response DTO |
| src/main/java/com/wallettransfer/dto/responsedto/TransferResponseDTO.java | Transfer response DTO |
| src/main/java/com/wallettransfer/dto/responsedto/ResponseDTO.java | Standard API response envelope |
| src/main/java/com/wallettransfer/dto/responsedto/LedgerEntryResponseDTO.java | Ledger entry response DTO |
| src/main/java/com/wallettransfer/dto/requestdto/WalletBalanceRequestDTO.java | Balance request DTO + validation |
| src/main/java/com/wallettransfer/dto/requestdto/TransferHistoryRequestDTO.java | Transfer history request DTO + validation |
| src/main/java/com/wallettransfer/dto/requestdto/CreateTransferRequestDTO.java | Create transfer request DTO + validation |
| src/main/java/com/wallettransfer/controller/WalletController.java | Wallet read-only endpoints (balance/history) |
| src/main/java/com/wallettransfer/controller/TransferController.java | Transfer creation endpoint |
| src/main/java/com/wallettransfer/constants/ResponseConstant.java | Business response codes |
| src/main/java/com/wallettransfer/constants/ApplicationConstants.java | Common status/message constants |
| src/main/java/com/wallettransfer/constants/ApiConstants.java | API path constants |
| src/main/java/com/wallettransfer/configuration/SwaggerConfig.java | OpenAPI configuration |
| src/main/java/com/wallettransfer/configuration/GlobalRestExceptionHandler.java | Global exception handling to ResponseDTO |
| src/main/java/com/wallettransfer/builder/ResponseBuilderFactory.java | ResponseDTO builder + responseCode parsing |
| sonar-project.properties | SonarQube analysis configuration |
| README.md | Project documentation and run/test instructions |
| postman/Wallet-Transfer-Service.postman_collection.json | Postman collection for API scenarios |
| pom.xml | Maven project definition + dependencies + JaCoCo |
| .vscode/settings.json | Editor configuration |
| .idea/.gitignore | IntelliJ ignore rules |
| .gitignore | Repo ignore rules (Java/Maven artifacts) |
| .github/workflows/ci.yml | GitHub Actions CI workflow running Maven verify |
Files not reviewed (1)
- .idea/.gitignore: Generated file
Comment on lines
+4
to
+5
| profiles: | ||
| active: local |
Comment on lines
+12
to
+13
| sonar.exclusions=**/WalletTransferServiceApplication.java | ||
| sonar.test.exclusions=**/* |
Comment on lines
+3
to
+10
| import org.springframework.boot.SpringApplication; | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| import org.springframework.boot.autoconfigure.domain.EntityScan; | ||
| import org.springframework.context.annotation.ComponentScan; | ||
|
|
||
| @ComponentScan(basePackages = {"com.wallettransfer.*"}) | ||
| @EntityScan("com.wallettransfer.*") | ||
| @SpringBootApplication |
Comment on lines
+32
to
+34
| private static boolean isSuccessCode(int code) { | ||
| return code >= 20000 && code <= 100000; | ||
| } |
Comment on lines
+3
to
+8
| import jakarta.servlet.http.HttpServletRequest; | ||
| import org.springframework.lang.Nullable; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| @Service | ||
| public interface LogService { |
| try (ExecutorService executor = Executors.newFixedThreadPool(2)) { | ||
| CountDownLatch ready = new CountDownLatch(2); | ||
| CountDownLatch start = new CountDownLatch(1); | ||
| List<String> responses = new ArrayList<>(); |
| try (ExecutorService executor = Executors.newFixedThreadPool(2)) { | ||
| CountDownLatch ready = new CountDownLatch(2); | ||
| CountDownLatch start = new CountDownLatch(1); | ||
| List<String> outcomes = new ArrayList<>(); |
Comment on lines
+104
to
+113
| List<TransferResponseDTO> history = | ||
| transferIds.stream() | ||
| .map( | ||
| transferId -> { | ||
| Transfer transfer = transfersById.get(transferId); | ||
| List<LedgerEntry> entriesForTransfer = | ||
| ledgerEntryRepository.findByTransfer_Id(transferId).stream() | ||
| .sorted( | ||
| Comparator.comparing(entry -> entry.getEntryType().name())) | ||
| .toList(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request — Wallet Transfer Service
Summary
Implemented a Wallet Transfer Service using Spring Boot 3.3.7 and Java 21.
The service supports wallet-to-wallet transfers with:
PENDING → PROCESSED | FAILEDAPIs delivered
POST /v1/transfers— creates an idempotent wallet-to-wallet transferPOST /v1/wallet-balance— returns wallet balancePOST /v1/transfer-history— returns transfer history for a walletAI disclosure
1. Tool used
I used Cursor as an AI-assisted IDE during development.
2. How I generally used the tool
I used Cursor as a development assistant for scaffolding, boilerplate generation, review support, test expansion, documentation, and SonarQube-related improvements.
The main architecture and implementation decisions were made and reviewed by me, including:
All generated or assisted code was manually reviewed, adjusted where needed, and validated by running the test suite.
3. Session transcript / prompts
I used Cursor through multiple short development iterations. The AI assistance was mainly used for:
ResponseExceptionpatternCursor was used as an assistant, while the final implementation, validation, and review were done by me.
Schema Design
The service uses four main tables.
walletsStores wallet details and current balance.
wallet_idbalanceversioncreated_atupdated_atConstraints:
wallet_idas primary keybalance >= 0@VersiontransfersStores transfer requests and their status.
ididempotency_keyfrom_wallet_idto_wallet_idamountstatusPENDING,PROCESSED, orFAILEDfailure_reasoncreated_atupdated_atConstraints and indexes:
idempotency_keyfrom_wallet_id <> to_wallet_idamount > 0from_wallet_id,to_wallet_id, andstatusledger_entriesStores double-entry ledger records for successful transfers.
idwallet_idtransfer_identry_typeDEBITorCREDITamountcreated_atFor each successful transfer:
DEBITentry is created for the source walletCREDITentry is created for the destination walletConstraints and indexes:
(transfer_id, wallet_id, entry_type)amount > 0transfer_idandwallet_ididempotency_recordsStores cached responses for idempotent requests.
ididempotency_keytransfer_idrequest_hashresponse_statusresponse_coderesponse_messageresponse_payloadcreated_atupdated_atConstraints:
idempotency_keytransfersIdempotency Strategy
The transfer API supports safe retries using an
idempotencyKey.The request hash is generated from:
fromWalletIdtoWalletIdamountIf the same idempotency key is used again:
For a new transfer:
PENDINGstatus.PROCESSED.idempotency_records.For business failures such as insufficient funds:
FAILED.Unique constraints on
transfers.idempotency_keyandidempotency_records.idempotency_keyensure concurrent duplicate requests cannot create multiple transfers.Concurrency Strategy
Concurrency safety is handled using database row locking and transaction boundaries.
Key points
PESSIMISTIC_WRITE.This prevents double spending when multiple transfers try to debit the same wallet at the same time.
Concurrency is verified through integration tests for:
How to Run
Prerequisites
Checkout branch
Run locally with H2
Run with PostgreSQL profile
Set the following environment variables:
URLs
Base URL:
Swagger UI:
Postman collection:
How to Test
Run all tests:
mvn testRun full verification with coverage:
Test coverage includes
Manual testing can be done by importing the Postman collection and running the included scenarios.
Tradeoffs / Assumptions
ResponseDTOresponse envelope.Checklist