The operator does not introduce buyers to sellers and take a fee. It buys in its own name. So when a supplier accepts a quote, the operator has made a purchase, and the trade has to travel from quote to acceptance to a record carrying everything settlement and reporting need. No step can silently drop and no step can fire twice. A trade that exists in one system and is missing from another is not a reporting inconvenience. It is material the operator owns, or money it owes, that nobody is tracking.
When the platform is the buyer, every accepted quote is a contract it has signed.
There are two ways to stand in the middle of a trade. A marketplace introduces a buyer to a seller. They agree terms with each other, the platform takes a fee, and if the deal goes wrong the dispute is between the two of them. A principal counterparty works differently. It buys from the seller in its own name and later sells to a buyer in its own name. For the time in between it owns the goods, carries the risk that the price moves, and is the party everyone else has a contract with.
That difference changes what a quote is. On a marketplace a quote is information. From a principal, a quote is an offer made with the principal's own money, and acceptance turns it into a contract. Trading firms call the moment a deal enters their books trade capture. Everything that follows reads from the captured record: settlement, which is the actual exchange of material and money, and reporting, which tells the business what it holds, what it owes and what it is owed.
Two engineering ideas carry this kind of system. The first is a state machine, a list of the states a trade can be in and the only moves allowed between them. The second is idempotency, the property of an operation that has the same effect whether it runs once or several times. Networks redeliver messages and people tap buttons twice. A binding action that is not idempotent will, sooner or later, happen twice, and for a principal that means buying the same material twice.
Every step was already there somewhere: a quote, an acceptance, a set of checks, an execution, a record. What was missing was the connection between them. That makes the platform's job a reliable state machine over an auditable trade record, and the screens are the smallest part of it. Acceptance is binding, so the transition that records it has to be correct the first time and harmless every time after, including the second tap from a supplier on a slow phone connection.
Trading systems lose trades in the gaps between steps that each work.
Each step in a trade usually works when tested alone. A quote screen shows a price. An acceptance endpoint records a yes. A settlement job pays what it finds. The failures live between them: a crash after the acceptance is saved and before the trade is, a message delivered twice, a job that reads a row while another process is halfway through changing it. Testing each screen in turn never exercises any of those.
The second pattern is duplication of truth. Settlement keeps its own table because it needs different fields. Reporting keeps another because it needs them summarised. Each copy is updated by its own code, and the copies drift. When they disagree there is no way to say which is right, because neither one was ever the original.
The reflex, and the fix.
Screens calling services as needed
It matches how the work looks from the outside, and each screen is simple on its own.
Nothing in that design makes a transition atomic or repeatable. Acceptance and the trade record can drift apart, and a double tap on a phone can execute the same trade twice.
A trade state machine with idempotent transitions
Explicit states and transitions to design up front, and an append-only record in place of a row that gets edited.
A trade that is in exactly one state at a time, an acceptance that can be retried safely, and a single record that settlement and reporting both read from.
Model the trade, because its states are what the business is accountable for. The screens are ways of looking at those states.
Named states with defined moves
Quoted, accepted, executed, recorded and rejected are states, and the transitions between them are listed. A trade cannot be both accepted and rejected because no transition leads there. An impossible combination is unrepresentable, which is a stronger guarantee than unlikely.
Idempotent acceptance
Each acceptance carries a key. A retry, a double tap or a request the network delivers twice arrives with the same key and reaches the same single execution.
An append-only trade record
Each event is written once and never edited. That is what lets the record serve as evidence of what happened. A row that gets edited only says what somebody last typed.
Settlement and reporting as projections
Both views are computed from the same record and keep no copies of their own. They cannot disagree about what happened, because there is only one account of it.
A missing rule stops the trade
When a trade reaches a situation that no written rule covers, it halts and goes to a person. The platform does not improvise a policy at the moment a binding decision is being made.
What each choice cost, and what it bought.
AModelling the trade before the screens
The brief read like a set of screens, and screens are what a client can see progress on. Starting from the states a trade can be in, and the allowed moves between them, delayed the visible part of the work. It bought something the screens could never provide on their own: a trade that cannot be in two states at once, and a single place where every move is checked. The screens then became thin, because the decisions they used to make now live in the transitions.
BPutting the duplicate check at the server
It is tempting to stop double execution in the interface, by greying out the button after the first tap. That helps the person and does nothing about network retries, redelivered messages or a second device. Giving each acceptance a key and having the server treat a repeated key as a request for the existing result costs a little design up front. It makes acceptance safe to retry from anywhere, which is the only kind of safe that holds on a phone.
CAppending to the record and never editing it
An editable trade row is simpler to build and quicker to query. It also destroys the evidence of how a trade reached its present state, which is the first thing anyone asks for when a settlement is disputed. An append-only record costs more storage and a little more work to read the current state. In exchange, every question about the past can be answered from the record itself, and settlement and reporting can be computed from it without keeping copies.
DStopping on the case no rule covers
When the platform meets a situation that the written rules do not address, it can guess or it can stop. Guessing keeps trades flowing and quietly commits the operator to decisions nobody made. Stopping sends the trade to a person and makes the missing rule visible, so it can be written down. That is slower on the day and it is how the rule set grew to match what the trader actually did.
A single headline number hides where a system fails. This work was scored on the dimensions that actually decide whether it holds in production, measured on real, held-out cases rather than the demo path.
- Accept, then retrythe same key must return the same single trade
- Double submit from a phonetwo taps must produce one execution
- Fail between transitionsa trade must land in a defined state, never half of one
- Check the record for settlementeverything settlement needs must already be in it
- Compare the two viewssettlement and reporting must agree because they share a source
No measured figures are published for this engagement. Outcomes are described qualitatively.
Screens that mostly work, an acceptance that occasionally executes twice, and a settlement view that disagrees with the reporting view with nobody able to say which one is right.
A trade state machine with idempotent transitions over an append-only record. Settlement and reporting are two readings of the same history, so the question of which copy is right never arises.
What it owns, and what it hands to a person.
The record is only as complete as the rules feeding it. A rule the trader held in their head and never said aloud is a gap in the platform until somebody writes it down. Finding those rules was part of the work, and could not be finished before the work started. Trades agreed outside the platform are outside it entirely, and no amount of internal correctness brings them in.
Any flow where acceptance is binding, from quote to accept to settle, wants the same four things: an explicit state machine, idempotent transitions, a record that is appended to and never edited, and downstream views computed from it. Insurance binding, purchase orders and reservations all share this shape.
A principal counterparty buys and sells in its own name, so every accepted quote on its platform is a contract it is bound by, and a software defect becomes its own financial exposure.
Any binding action a user can trigger twice will eventually be triggered twice. Make it idempotent with a key the server checks, because no client can promise to send exactly once.
Name the states a record can be in and the moves allowed between them. A combination no transition reaches cannot occur, which beats detecting it afterwards.
Keep one record of what happened and compute every other view from it. Two copies of the truth will drift, and nothing can say which one is right.
When a process is automated, the rules people applied without noticing are the ones that go missing. Treat finding and writing them down as part of the build.