feat(ledger): add rest api for accounts and transactions - #106
Merged
Conversation
Thin controllers over the existing application services: create, get and balance for accounts and post for transactions. Adds request/response DTOs with bean validation, an Idempotency-Key servlet filter that reuses IdempotencyService, RFC 7807 problem responses mapping domain conflicts to 409 and invariant violations to 422, JWT resource-server security with the subject as actor, and a served OpenAPI document. Transaction get and reverse are deferred to #105 (need new service methods). Closes #35 Closes #36 Closes #37 Closes #40 Closes #43
response_body is a jsonb column normalized by postgres, so replay is logically identical rather than byte-identical; compare parsed json trees.
This was referenced Jun 15, 2026
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
Thin REST edge over the existing ledger application services (closes E-01 backend).
POST /v1/accounts,GET /v1/accounts/{id},GET /v1/accounts/{id}/balancePOST /v1/transactionsIdempotencyFilter(Idempotency-Key guard + body buffering) reusingIdempotencyServiceapplication/problem+jsonmapping: 404 / 409 (duplicate, idempotency) / 422 (double-entry, currency, domain) / 503 (concurrency, Retry-After) / 400actor = jwt.subject; swagger + api-docs + health permittedDesign notes
IdempotencyStore.runOrReplayis@Transactionaland runs the action inside the tx, which the write services join (atomic exactly-once). A filter wrapping the whole dispatch would dead-end on rollback-only marking after@RestControllerAdviceconverts an inner exception, so the filter only guards the header and buffers the body;execute()is called in the controller (inside the tx-joining call stack), where exceptions propagate cleanly to the advice and leave no idempotency row.@Componentmapper consistent with the persistence adapters.Tests
@WebMvcTestslices for both controllers (status/mapping/idempotency/validation/auth),IdempotencyFilterunit test, mapper test. 118 unit tests green locally.LedgerApiSmokeIT(CI-only, Testcontainers): full context loads,/v3/api-docsserved, idempotent replay returns identical bytes, unauthenticated -> 401.Transaction get/reverse deferred to #105.
Closes #35
Closes #36
Closes #37
Closes #40
Closes #43