feat(ledger): add account and idempotency application services - #103
Merged
Conversation
AccountService (create, get, rename, status transitions) over the existing domain state machine, with close-requires-zero-balance enforced by reading account_balances. IdempotencyService is DB-backed with a 24h TTL: it replays a stored response on a repeated key, raises a conflict on a key reused with a different request, refreshes an expired key, and is exactly-once under concurrency via the key_hash primary key (winner runs, loser retries and replays). Account domain<->entity conversion is a hand-written adapter since MapStruct cannot construct the value-class aggregate. @transactional lives only in the impls; reads are readOnly; no external calls inside transactions. Unit tests cover both services and the adapter; an integration test exercises the account round-trip and idempotency paths on pg17. Closes #18 Closes #20 Closes #26 Closes #44
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.
What
F-01.3 application services, PR 2a of 2 (Account + Idempotency; PR 2b will add Transaction posting + Balance).
create,get,rename,changeStatus. Status transitions use the existing domain state machine (AccountStatus.canTransitionTo);changeStatus(.., CLOSED)is blocked unless the account holds no non-zero balance (account_balancesread).(key, body); raisesIdempotencyConflictExceptionon a key reused with a different request; refreshes an expired key. Exactly-once under concurrency via thekey_hashPK - the winner runs the action and commits, a racing loser fails the insert, rolls back, and retries in a fresh transaction to replay the committed response. SHA-256 hashes are computed at runtime (no hardcoded digests).@Transactionalonly in the impls;getisreadOnly; no external I/O inside transactions.Tests
Unit (MockK, 16 green locally): account create/get/rename/state-machine/close-guard, idempotency replay/conflict/expiry/race-signal/retry, adapter identity. Integration (
@DataJpaTest+@Import, pg17 on CI): account create+read-back, idempotent replay (action runs once), conflict, close-blocked-by-balance, close-empty.The full idempotency concurrency stress test is tracked separately as #57.
Verification
Local:
:test(16/16 new unit tests green),:spotlessCheck,:detekt,:compileIntegrationTestKotlin,:assembleall BUILD SUCCESSFUL. Integration tests run on CI (Testcontainers); env-blocked under local WSL Docker.Closes #18
Closes #20
Closes #26
Closes #44