The ATM, a system best understood as a conversation

Published . By the DiagramDesk team.

Most classic systems are about data: books, orders, patients. The cash machine is about an exchange of messages between three parties, and it holds very little data of its own. That is why this page starts with the sequence diagram, and why the ER model at the end is the smallest in the series.

The withdrawalUse casesClassesDataData flowMistakes

A withdrawal, message by message

Three lifelines are enough: the customer, the machine and the bank's system. The machine never decides on its own whether a PIN is right or whether the account can pay. In this model it asks, and the bank answers, which is what lets a rule such as "three wrong PINs and the card is kept" hold across every machine rather than being reset by walking to the next one. The three-try limit is this example's assumption; banks set their own.

A loop for the PIN tries, then two outcomes in one alt frame.loop [Until the PIN is accepted, three tries at most]alt [Refused a third time]Insert cardAsk for the PINEnter PINVerify card and PINAccepted or refusedKeep the cardCard kept, contact your bankAsk for an amountAuthorise the withdrawalApproved, account debitedCount out the notesReturn the cardPresent the cashCash takenCustomerATMBank system[Accepted]
A loop for the PIN tries, then two outcomes in one alt frame.Open in the editor

Three details in that diagram carry marks. The PIN tries are one loop frame with a condition, not three copies of the same three messages. The two outcomes, card kept or cash paid, sit in one alt frame with a guard on each half. And the card comes back before the cash does.

That last one is a design decision with research behind it. Byrne and Bovair, writing in Cognitive Science in 1997, named the mistake a "postcompletion error", and their typical example is "leaving one's card in the automatic teller after withdrawing cash": a step left over once the main goal is met. Ordering the messages so the cash comes last removes the leftover step, and a sequence diagram is where that ordering is written down. The final message, telling the bank the cash was taken, is there for the case where it is not: the notes jam or are never collected, and the debit has to be reversed.

Use cases: one identification step, shared

Every customer use case starts by identifying the customer with a card and a PIN, so it is drawn once, as Identify the customer, and «include»d three times. Keep the card extends that step: it happens only on the third refusal. The bank's system is attached to the identification step because that is where the machine calls on it; attaching it to every use case would say the same thing three more times.

Three customer goals, one shared step, and the machine's own operator.ATM«include»«include»«include»«extend»Withdraw cashCheck thebalanceChange the PINRefill the cashStart or stop themachineIdentify thecustomerKeep the cardCustomerCash operatorBank system
Three customer goals, one shared step, and the machine's own operator.Open in the editor

The cash operator is easy to forget, and a model without one has a machine that never runs out of money. Refilling the cash and starting or stopping the machine are real goals for a real person, and they are what feed the reconciliation further down.

Classes: hardware, sessions and a family of transactions

The class diagram falls into three layers. The machine is composed of its devices (a card reader and a cash dispenser are drawn here; a keypad, screen and printer would join them the same way). A session lasts from card in to card out and counts the PIN attempts. And a session performs transactions, of which there are several kinds that differ only in what run() does.

Transaction is abstract; each kind of transaction implements run() its own way.1 · runs · 0..11 · performs · 0..*asksATM+atmId: String+cashOnHand: Money+startSession(card) SessionCardReader+readCard() Card+eject()+retain()CashDispenser+canPay(amount) bool+dispense(amount)Session+attempts: int+identify(pin) bool+end()«abstract» Transaction+run()*Withdrawal+amount: Money+run()BalanceEnquiry+run()PinChange+run()«interface» BankLink+verify(card, pin) bool+authorise(card, amount) bool
Transaction is abstract; each kind of transaction implements run() its own way.Open in the editor

Transaction is marked abstract and its run() carries an asterisk, Mermaid's mark for an abstract method, so no one can create a plain Transaction, only a Withdrawal, a Balance enquiry or a PIN change. A new kind of transaction then means a new subclass, not another branch in an if-statement. Bank link is an interface: the session depends on something that can verify and authorise, not on how the bank's network actually does it, which is also what makes the session testable without a bank.

What the machine itself keeps

An ER model for an ATM is mostly the bank's data seen from one machine, plus a little of the machine's own. Accounts and cards belong to the bank. The machine's contribution is its journal of transactions and the state of its cassettes: how many notes of which denomination are left in each slot.

Cassettes and refills are the machine's own data; the rest is the bank's.is reached byis used inrecordsholdsis topped up inACCOUNTstring account_no PKdecimal balanceCARDstring card_no PKstring account_no FKdate expires_onstring statusATM_TRANSACTIONint txn_id PKstring atm_id FKstring card_no FKstring kinddecimal amountstring outcomedatetime atATMstring atm_id PKstring locationCASSETTEstring atm_id PK, FKint slot PKint denominationint notes_leftREFILLint refill_id PKstring atm_id FKint slot FKint notes_addeddatetime at
Cassettes and refills are the machine's own data; the rest is the bank's.Open in the editor

The ATM transaction records every attempt, including refused ones, with its outcome, because the journal is what gets checked when a customer says the machine took money and gave nothing. The cassette's key is the machine plus the slot number, since slot 2 means nothing without knowing which machine it is in. That makes ATM to Cassette the one identifying relationship, drawn with a solid line; every other line is dashed, because Card, ATM transaction and Refill each have a key of their own.

Data flow: where the numbers must agree

The level 1 DFD earns its place with process 3.0. Every payout writes a journal entry and reduces a cassette's count, and at the end of the day the operator's count of the notes left, the journal and the cassettes' own counts have to agree. That reconciliation is invisible in the use cases and the sequence, and it is the kind of thing a DFD is for.

The bank's system is outside the boundary, so it appears as an entity.card and PINPIN checkverdictamountauthorisationapprovalcash, cardjournal entrynotes takennotes loadedday's payoutsnotes leftbalance reportCustomerCash operator1.0Identify customer2.0Pay out cash3.0Balance the cashBank systemD1 Machine journalD2 Cassettes
The bank's system is outside the boundary, so it appears as an entity.Open in the editor

Mistakes to avoid

  1. A balance column on the ATM, or an Account table the machine owns. The machine asks; the bank knows.
  2. Identification repeated inside every use case instead of one «include»d step.
  3. The PIN attempts drawn three times in the sequence diagram rather than as a loop with a limit.
  4. One Transaction class with a "type" field and every behaviour switching on it.
  5. Only the happy path. A refused PIN, a refused withdrawal and a jam each need somewhere to go.
  6. A line straight from the customer to the bank. In this system the customer only ever talks to the machine.

If your brief adds deposits, add a deposit use case and a cassette for accepted notes; the rest stands. If it asks for a state diagram, the session is the natural subject: waiting for a card, waiting for a PIN, choosing a transaction, and ending.