Skip to content

Fintech repository evidence map

Reviewed: 2026-08-22 (Asia/Dubai)

Reference repository (read-only): /Users/mustafa/Documents/Codex/2026-08-22/c/fintech-project

This is the audit trail for deliverable/01-repository-case-study.md. All code locators below are relative to the reference repository. The review uses three labels deliberately:

  • Observed: a fact visible at the cited file and line.
  • Documented: an AWS or pattern semantic controlled by the cited claim and source IDs in claim-register.md and source-ledger.md.
  • Inference: an architecture conclusion from observed code plus documented semantics; it is not a vendor guarantee.
PathObserved implementationExact repository routeBoundary / role
Order APIThe production HTTP API exposes POST /orders through an AWS proxy integration to the order-handler alias.infra/environments/production/main.tf:419-477Synchronous client/API/Lambda request path. API success is not downstream business completion (C51/A53).
Input and identityThe handler parses JSON, validates the order, generates a new order ID, and builds a PENDING order record.code/lambdas/order-handler/handler.ts:114-153; code/shared/validation/schemas.ts:15-27Command validation and operational order-state creation.
Idempotency lookupA supplied Idempotency-Key is read before order processing; lookup failure is explicitly fail-open.code/lambdas/order-handler/handler.ts:219-241Best-effort response cache, not business idempotency (C40/F11).
Order persistenceThe order is written to the main DynamoDB table with a standalone PutCommand.code/lambdas/order-handler/handler.ts:155-165First durable side effect of order submission.
Idempotency persistenceA cached 201 response is conditionally put in a separate table after the order write and before publication; any store error is logged and ignored.code/lambdas/order-handler/handler.ts:81-104,167-181; infra/environments/production/main.tf:617-658Separate write and cache boundary. TTL is cleanup, not a correctness deadline (C32/A23).
OrderPlaced publicationThe handler calls PutEvents after both database writes, catches thrown errors, but does not retain or inspect the API result.code/lambdas/order-handler/handler.ts:183-212Synchronous producer API call followed by intended asynchronous routing. PutEvents can partially fail; even zero failed entries plus EventId is only producer-API acknowledgement and can accompany a nonexistent-bus drop (C08/A06,A82).
Event bus and order targetThe custom bus/rule configuration intends to route trading.OrderPlaced to trade-executor. The reusable target attaches a per-rule SQS DLQ ARN, but its module creates no queue resource policy granting EventBridge sqs:SendMessage.infra/environments/production/main.tf:345-359; infra/modules/eventbridge/main.tf:29-38,62-95; bounded absence check belowEventBridge target delivery is distinct from Lambda runtime processing. The declared DLQ is not a usable recovery path until the queue policy exists (C05-C07,C57/A01,A04,A05,A81).
Trade executionThe handler validates the event, generates a trade ID, and calculates a random simulated price using binary floating point.code/lambdas/trade-executor/handler.ts:65-96This is a simulation inside Lambda, not an order-book/matching-engine implementation.
Trade persistenceA trade item keyed by the order ID is conditionally inserted.code/lambdas/trade-executor/handler.ts:83-104Duplicate trade-row guard for that key; not an atomic business workflow.
Order mutationA separate update changes the order to EXECUTED; it has no condition requiring the previous state to be PENDING.code/lambdas/trade-executor/handler.ts:106-118Second DynamoDB side effect.
TradeExecuted publicationA third standalone operation sends an integration event and ignores the PutEvents result.code/lambdas/trade-executor/handler.ts:120-148Producer/database dual-write boundary (C08, C22, C38).
Notification fan-outOne EventBridge rule targets notification-sender; the handler validates and publishes JSON to an SNS topic.infra/environments/production/main.tf:360-370; code/lambdas/notification-sender/handler.ts:43-90Derived client-notification path, not authoritative financial state (C46).
Portfolio fan-outA separate rule targets portfolio-updater and declares a separate SQS DLQ ARN. The handler reads a portfolio, applies the trade, and conditionally replaces the item using its current version, retrying conflicts three times. No queue resource policy is declared for this DLQ either.infra/environments/production/main.tf:565-615; code/lambdas/portfolio-updater/handler.ts:103-198; bounded absence check belowAsynchronous mutable projection. The event has no projection inbox/deduplication record or source sequence, and the declared DLQ lacks its required write permission.
Trade queryGET /trades invokes trades-reader, which queries GSI1 by user and time prefix with a maximum result limit of 100.infra/environments/production/main.tf:695-729; code/lambdas/trades-reader/handler.ts:45-97Read path through an eventually consistent GSI (C21/A19). No pagination token is returned.
Daily snapshotA scheduled rule invokes portfolio-snapshot. One filtered Scan result is converted in memory to one JSONL object and written to S3.infra/environments/production/main.tf:373-393; code/lambdas/portfolio-snapshot/handler.ts:64-107Derived batch/export path. It is not a proven complete point-in-time financial snapshot.
Event archive and target DLQsThe EventBridge module declares a 30-day archive, 14-day SQS queues, and queue-depth alarms; production separately declares the portfolio queue/alarm. Neither Terraform path declares aws_sqs_queue_policy or an events.amazonaws.com sqs:SendMessage grant, so EventBridge cannot be shown able to populate those queues. The depth alarms can remain zero while InvocationsFailedToBeSentToDLQ rises.infra/modules/eventbridge/main.tf:15-60,76-95; infra/environments/production/main.tf:575-605; bounded absence check belowArchive is a bounded replay source; the SQS queues are only intended holding paths until permissions are fixed. Neither is reconciliation (C43,C57/A81).
Manual redrivedlq-replayer receives at most ten messages, reconstructs PutEvents entries, deletes after the SDK call returns, and does not inspect per-entry failures or independently validate the named bus.code/lambdas/dlq-replayer/handler.ts:61-145Operator-triggered redrive. HTTP 200, zero failures, or EventId is only producer-API acknowledgement; deletion needs validated bus configuration, durable replay identity, and downstream reconciliation (C08/A06,A82).
ObservabilityEvery Lambda module enables active X-Ray tracing, a 14-day log group, and a Lambda error alarm; handlers use Powertools Logger and propagate a correlation ID in events.infra/modules/lambda/main.tf:12-70; code/shared/types/events.ts:1-12Technical telemetry exists; business-correctness and reconciliation metrics are not apparent (C48, C50).
DeploymentLambda aliases are the integration targets and CodeDeploy declares a 10%-for-five-minutes canary only for order-handler.infra/modules/lambda/main.tf:42-49; infra/environments/production/main.tf:479-517Deployment safety is narrower than end-to-end workflow compatibility.
IAMProduction defines per-function roles: order handler can write/read DynamoDB and publish events; trade executor can write/update/read and publish; notifier can publish SNS; snapshot can scan/write S3; portfolio updater can get/put; trades reader can query; replayer can receive/delete from named DLQs and publish. The events.amazonaws.com principals at lines 391, 401, 410, and 613 are Lambda resource permissions, not SQS queue policies.infra/environments/production/main.tf:102-239,386-411,521-549,608-615,645-773Function/resource permissions are visible, but the DLQ sqs:SendMessage grants are absent; this is not a complete security audit.

Solid arrows are synchronous request/API calls from the caller's perspective. Dashed arrows are scheduled or broker-driven asynchronous handoffs. Cylinders are stores; double-bracket nodes are holding/replay paths.

Diagram text source
flowchart LR
Client[Client] -->|POST /orders| API[API Gateway HTTP API]
API --> OH[order-handler Lambda]
OH -->|Put order| Main[(Main DynamoDB table)]
OH -->|Put cached 201| Idem[(Idempotency DynamoDB table)]
OH -->|PutEvents API| Bus{Custom EventBridge bus}
Bus -. OrderPlaced target .-> TE[trade-executor Lambda]
TE -->|Put trade; Update order| Main
TE -->|PutEvents API| Bus
Bus -. TradeExecuted target .-> PU[portfolio-updater Lambda]
PU -->|Get; conditional Put| Main
Bus -. TradeExecuted target .-> NS[notification-sender Lambda]
NS --> SNS[SNS notification topic]
Client -->|GET /trades| API
API --> TR[trades-reader Lambda]
TR -->|Query GSI1| Main
Schedule[Daily schedule] -.-> PS[portfolio-snapshot Lambda]
PS -->|filtered Scan| Main
PS --> S3[(S3 JSONL snapshots)]
Bus -. failed target delivery .-> DLQGap{{DLQ send denied:<br/>queue resource policy absent}}
DLQGap -. InvocationsFailedToBeSentToDLQ .-> Metric[CloudWatch metric]
DLQGap -. after required policy only .-> DLQ[[Declared per-target SQS DLQs]]
Ops[Operator/manual invoke] --> DR[dlq-replayer Lambda]
DLQ -->|Receive/Delete only if populated| DR
DR -->|PutEvents API| Bus

Exact topology routes: API and routes infra/environments/production/main.tf:419-477,695-729; bus rules and targets infra/environments/production/main.tf:345-411,565-615; generic DLQ declarations infra/modules/eventbridge/main.tf:27-95; missing-policy absence check below; schedule infra/environments/production/main.tf:373-393.

Data/componentAs-built roleAuthority conclusion
Order rowMutable command status (PENDING, then EXECUTED).Observed: it is the only persisted order-state row. Inference: it is the apparent operational authority for order status, but the code does not enforce a full order state machine or reservations.
Trade rowOne conditional record per order key containing the simulated fill.Observed: it is the apparent persisted execution fact. Inference: without matching, clearing/settlement, or ledger postings, it is not sufficient authority for cash or securities ownership.
Portfolio rowMutable holdings and cost-basis view updated from TradeExecuted.Inference: a derived projection. It cannot safely enforce available-balance or ownership invariants (C46/F17,F18).
GSI1 trade viewReplicated DynamoDB index queried by user.Documented: GSI reads are eventually consistent (C21/A19). It is a query projection, not an independent authority.
Idempotency rowCached HTTP status/body with TTL.A response cache. It is neither the protected order mutation nor proof of valid event routing or downstream progress.
EventBridge bus/archive and SQS DLQsIntended transport, bounded archive, and declared terminal-delivery queues.Not a ledger or reconciliation system; the current SQS DLQs are not shown writable because their EventBridge queue policies are absent (C43, C47, C57).
S3 snapshotDaily JSONL derived from mutable portfolios.Analytics/audit convenience output; current implementation does not prove completeness or point-in-time consistency.
SNS notificationUser-facing derivative of a trade event.A notification, never evidence that the authoritative business process completed.
BoundaryWhat success provesWhat it does not proveRequired design response
API Gateway to order-handlerThe synchronous integration returned an HTTP response.That a trade, reservation, ledger posting, projection, or notification completed (C51/A53).Return an explicitly scoped acceptance state and expose order status.
DynamoDB order PutCommandThat one order-row write returned successfully.Publication to EventBridge or creation of a trade. DynamoDB transactions cannot include EventBridge (C22/A20).Commit state plus an outbox record locally, then publish asynchronously (C38).
PutEvents producer callHTTP 200, zero failed entries, and EventId establish only producer-API acknowledgement for the named request.Valid bus existence/configuration, matching/routing, target delivery, consumer durability, database effects, or business completion; a nonexistent bus can return those response signals and drop the event (C08/A06,A82).Inspect entries, validate bus/configuration independently, retain stable replay identity, and reconcile downstream effects.
EventBridge bus to Lambda targetEventBridge owns target-delivery retry according to the target policy (C05/A05).Function-runtime retry, handler rollback, one business effect, or a working target DLQ. The current queues lack the required EventBridge send policy (C57/A81).Configure/observe target delivery separately; add rule-scoped queue policies and alarm both delivery failures and InvocationsFailedToBeSentToDLQ.
Lambda asynchronous invocation/runtimeIts retry and terminal destination are a separate boundary (C06/A01,A04,A05).Atomicity across the handler's several SDK calls.Configure runtime failure handling and make each business transition idempotent.
Trade handler's DynamoDB callsEach returned write succeeded individually.Atomic trade creation plus order transition; neither call includes EventBridge.Use one DynamoDB transaction for compatible local items, with conditional state transition and outbox.
Portfolio optimistic lockThe replacement used the version read by that invocation.That the event was new or in causal order.Atomically store an inbox/event ID and expected business sequence with the projection update (C40-C41).
Target DLQ or event archiveThe archive is configured for bounded retention; an SQS message is retained only if EventBridge can write it. Current queue declarations do not establish that permission.A configured DLQ ARN, zero queue depth, diagnosis, safe redrive, exact business recovery, or reconciliation (C43,C57/A81).Add/test the queue policies, alarm failed DLQ sends, and own triage, correction, paced replay, idempotency, and reconciliation.
Business completionNo single as-built signal establishes matched execution, reservations, balanced postings, clearing/settlement, and reconciled projections.End-to-end financial correctness.Define authoritative domain boundaries, ledger postings, and independent reconciliation (C46-C48).

These IDs are mirrored in claim-register.md and the deliverable. Each row separates repository observation from controlled semantics and inference.

IDRequired issueExact observed routeControlled route
CS01DynamoDB-to-EventBridge dual write is non-atomic.code/lambdas/order-handler/handler.ts:155-165,167-181,183-208C22, C38 / A20, A21, F04
CS02Cached idempotency success can mask an order with no valid routing/downstream evidence.code/lambdas/order-handler/handler.ts:167-180,229-241C08, C32, C40 / A06, A23, A82, F11
CS03PutEvents partial failures and the nonexistent-bus acknowledgement boundary are ignored.code/lambdas/order-handler/handler.ts:193-205; code/lambdas/trade-executor/handler.ts:134-145; code/lambdas/dlq-replayer/handler.ts:114-133C08 / A06, A82
CS04Trade insert, order update, and publication are non-atomic.code/lambdas/trade-executor/handler.ts:83-118,120-145C22, C38 / A20, A21, F04
CS05A publication failure becomes unrecoverable through ordinary retry after the duplicate-trade guard.code/lambdas/trade-executor/handler.ts:79-104,120-145C08, C40 / A06, A82, F11
CS06Portfolio locking lacks event idempotency.code/lambdas/portfolio-updater/handler.ts:145-186; code/shared/types/dynamodb.ts:63-79C40-C41 / F11
CS07The portfolio depends on ordering that EventBridge does not establish.code/shared/types/events.ts:36-47; code/lambdas/portfolio-updater/handler.ts:145-178; infra/environments/production/main.tf:565-605C09, C34, C41 / A07, F01, F09, F24
CS08Target DLQ, DLQ-write permission, and Lambda asynchronous failure are different boundaries; the queue policies are missing.infra/modules/eventbridge/main.tf:29-38,73-95; infra/environments/production/main.tf:575-605; infra/modules/lambda/main.tf:12-70; bounded absence check belowC05-C07, C57 / A01, A03-A05, A81
CS09Daily snapshot is one unpaginated filtered Scan assembled in memory.code/lambdas/portfolio-snapshot/handler.ts:64-107C21, C24 / A19, A22; direct bounded DynamoDB export route
CS10Monetary values use binary floating point.code/shared/validation/schemas.ts:21-27; code/lambdas/trade-executor/handler.ts:55-63; code/lambdas/portfolio-updater/handler.ts:64-84,121-135; code/lambdas/portfolio-snapshot/handler.ts:50-61C47 / A41, F08, F17
CS11No authoritative append-only ledger or reconciliation mechanism is apparent.code/shared/types/dynamodb.ts:16-93; infra/modules/dynamodb/main.tf:1-60; repository-wide absence check belowC46-C48 / A32-A35, A41, F08, F17, F18
CS12Lambda is not automatically suitable for a latency-critical matching loop.code/lambdas/trade-executor/handler.ts:55-81; infra/modules/lambda/main.tf:12-31C49, C53 / A44-A51, A58-A60, F09, F19, F20

Repository-wide absence check used for CS11:

Terminal window
rg -n -i 'ledger|posting|reconcil|reservation|settlement|clearing' code infra

Result: no application implementation was found; the only unrelated match was the word “reserved” in comments describing future sort-key use. Absence is therefore stated as “not apparent,” not as proof that no external system could exist outside this repository.

Repository-wide absence check used for CS08:

Terminal window
rg -n 'aws_sqs_queue_policy|sqs:SendMessage' infra -g '*.tf'

Result: no match. A separate search for events.amazonaws.com finds only aws_lambda_permission principals in the production/staging composition files; none grants sqs:SendMessage on either declared DLQ. The queue resources and dead_letter_config blocks therefore show intent, not an operationally authorized EventBridge-to-SQS path.

Review notes outside the twelve-item acceptance set

Section titled “Review notes outside the twelve-item acceptance set”
  • The production and staging composition files are not identical. In production, dlq-replayer IAM includes the separately declared portfolio DLQ (infra/environments/production/main.tf:749-765); staging grants only the module-managed DLQs (infra/environments/staging/main.tf:697-722). This does not create a thirteenth case-study issue, but a deployment review should resolve the environment drift before relying on portfolio redrive in staging.
  • The case study does not infer authentication, WAF, CloudTrail, or KMS posture from their absence in the inspected application paths. Those belong to the later security chapter and require their own complete scope review.

Reading layout adapted from SSE reading notes by Mohammed Balila, MIT. Source manifest · Attribution