Specification · 001-position-core
Position keeping core
A hexagonal position keeper: trade events in, positions out, idempotent on tradeId.
The domain core: apply trade events to positions, idempotent on tradeId, behind ports so persistence is swappable. JDK-only domain, in-memory repository for this first milestone. The ports-and-adapters shape here is what lets a later feature (002) make the adapter the single writer.
- I Independent
- A self-contained feature in the position-keeper timeline.
- N Negotiable
- Outcomes contract; the mechanism is the design's.
- V Valuable
- Unlocks SC-001.
- E Estimable
- Plan derivable.
- S Small
- 4 requirements.
- T Testable
- Observable via its success criterion.
- Exec / PM
- §Context, §Success criteria.
- Reviewer
- §Requirements, §Open questions.
- Implementer
- §Requirements, §Data model.
1 · Context
A position keeper turns a stream of trade events into current positions. It must be idempotent (a redelivered trade cannot double-count) and keep the domain free of infrastructure so persistence can change later. This is the base every other feature builds on.
2 · User scenarios & testing
A trade updates a position. Booking a buy of 100 sets the position to 100.
Idempotent on tradeId. Re-booking the same tradeId leaves the position at 100.
3 · Requirements
tradeId was already applied MUST NOT change the position (idempotent).- Emitting events to consumers — the next feature.
- Durable persistence — in-memory here; JDBC later.
4 · Data model
- Position
- an instrument and a signed quantity; the running sum of applied trades.
- Trade event
- an instrument, a signed quantity, and a unique
tradeId(the idempotency key).
5 · Success criteria
tradeId leaves it unchanged.
Observed at
A unit test applying a trade, then the same trade again.
Observed at
An ArchUnit rule asserting the core package imports only JDK + domain types.
6 · Assumptions
- Whole-unit quantities (a signed
long); fractional lots are not in this timeline. - The ports-and-adapters shape chosen here is the boundary a later feature (002) will pin as the single writer of the store.
7 · Open questions
None outstanding.
8 · Conformance index
9 · Change log
- Status flipped Draft → Accepted on 18 Sep 2026 — zero remaining unchecked tasks (13/13); ./gradlew clean build green with PositionServiceTest (SC-001) and CoreRulesTest (SC-002) verified passing per author confirmation. Sibling bundle (REQ-LIFECYCLE-005).
- Reset to Draft — re-driven through tasks → implement → verify against a real Gradle build; the 2024–25 dates in docs/timeline.md remain the narrated history.
- Accepted — the hexagonal core with an in-memory repo; ports-and-adapters and a JDK-only domain.