Appointment Booking: a worked architecture, end to end
This is a worked example, not a real product. Example Clinic is a fictional practice of three staff that takes bookings through a widget on its own website. The system is invented; the records that describe it are written the way a real project's would be, and they point at one another, so you can follow a requirement to the decision that answers it, the contract that carries it across an interface, and the automated test that covers it. It exists to show how the records connect.
- 14requirements
- 7decisions
- 1solution design
- 3interface contracts
- 20acceptance criteria with an automated test (of 23)
How the pieces connect
Every record is a Markdown file in the public architecture-records repository, licensed CC BY 4.0. The demo is its own repository, with MIT-licensed code.
Requirements: what must be true
Written as "shall" statements with WHEN and THEN scenarios, in the form of this site's own OpenSpec proposals, so each one can be checked. They say what must hold, not how: the no-overlap guarantee, the five-minute hold, availability within 60 seconds, no message sent late.
Booking requirements
Booking without an account, the no-overlap guarantee, the five-minute hold, changing and cancelling, repeated requests.
5 requirements, 16 scenarios
Availability requirements
What is shown as free, how fresh it is (60 seconds), the live check on confirm, and times across zones and daylight saving.
4 requirements, 10 scenarios
Notification requirements
Confirmations and reminders, and what must hold when the message provider is down.
5 requirements, 13 scenarios
Decisions: how the design makes it true
Only major decisions are written up as architecture decision records: the ones that are hard or costly to reverse, that commit money or a vendor, or that fix a major quality. Each lists the options considered and its consequences; the design's section 3 adds what each one gives up. A smaller choice, such as the five-minute hold, is a requirement or a design note instead.
ADR-AB-0001: Embed the booking widget in an iframe, loaded by a small script
Status: acceptedDecided 2026-09-26.Cited by ICR-AB-0001.
ADR-AB-0002: Expose booking as a public REST API behind a gateway, identified by a widget key
Status: acceptedDecided 2026-10-02.Cited by ICR-AB-0001.Related requirements: booking requirements (reference).
ADR-AB-0003: Show availability from a synchronised copy, and check it live on confirm
Status: acceptedDecided 2026-09-26.Cited by ICR-AB-0001, ICR-AB-0002.Related requirements: booking requirements (relates to).
ADR-AB-0004: Build calendar connectors on each provider's own API, rather than buy an aggregation platform
Status: acceptedDecided 2026-10-02.Cited by ICR-AB-0002.Related requirements: availability requirements (reference).
ADR-AB-0005: Share one database across businesses, with row-level isolation
Status: acceptedDecided 2026-09-26.Cited by ICR-AB-0001.
ADR-AB-0006: Run on containers in one Australian region, recoverable into a second
Status: acceptedDecided 2026-10-02.Cited by ICR-AB-0001, ICR-AB-0003.
ADR-AB-0007: Hold the booking data in a managed PostgreSQL database
Status: acceptedDecided 2026-10-02.Cited by ICR-AB-0001, ICR-AB-0003.Related requirements: notification requirements (enables), booking requirements (reference).
Design: the solution architecture, in one document
The solution architecture design (version 1.4, status approved, last updated 2026-10-09) puts the requirements, the seven decisions and the contracts into one picture: the context, the components, three runtime scenarios, integration, data, security, deployment and recovery, cross-cutting concerns, and the risks it accepts. Its sections are 1. Executive Summary; 2. Business Context & Requirements; 3. Architecture Decisions (ADR Linkage); 4. System & Component Model; 5. Integration & Interface Design; 6. Data Architecture; 7. Security & Compliance Controls; 8. Deployment & Infrastructure; 9. Cross-cutting Concerns; 10. Risks, Assumptions & Technical Debt; 11. Glossary.
The flow the design depends on most: confirming a booking
- The widget sends the confirmation with an idempotency key, so a repeated request creates nothing new.
- The booking service asks the calendar connector to check that one time against the staff member's own calendar.
- If the provider answers and the time is taken, the customer is told so and offered the next free times. If it is free, the booking goes ahead.
- If the provider cannot be reached, the check is recorded as skipped and the booking still goes ahead. A double booking against an event the platform had not yet seen is possible; the design accepts that deliberately (ADR-AB-0003) and reconciles the calendar afterwards.
- The booking and its outbox rows are written in one transaction, and the widget is told it is confirmed.
- The outbox is relayed: the calendar connector writes the event once, and the notification worker sends the confirmation once.
The design also draws the system context (design section 4.3), two more runtime flows (a staff member's calendar changing, and a booking changing while the message provider is down), and, in section 4.5, places each component on the integration and data platform reference architectures (the Integration reference architecture on this site). Section 5.1 names the library pattern behind each of its four integrations; the contracts below show the three that have a contract.
Interface contracts: what crosses each boundary
An integration contract record states what one party provides and another depends on: purpose, parties, interaction, flows, errors, service levels, and numbered acceptance criteria. Each cites the decisions that shaped it and the library pattern it follows.ICR-AB-0001 (version 1.2.0) and ICR-AB-0003 (version 1.1.0) are agreed and ICR-AB-0002 (version 0.11.0) is proposed, as each states in its front matter. The Calendar synchronisation contract stays proposed until the platform lead agrees the renewal schedule and alert and they are tried against a real provider. Within the Booking API, extending and releasing a hold remain proposed (acceptance criterion 8).
ICR-AB-0001: Booking API
Synchronous request and responseVersion 1.2.0Status: agreed
How the booking widget, in a customer's browser, finds free times and holds, confirms, changes and cancels bookings.
- Follows the pattern
- Integration API Management (External)
- Cites
- ADR-AB-0001, ADR-AB-0002, ADR-AB-0003, ADR-AB-0005, ADR-AB-0006, ADR-AB-0007
- Acceptance criteria
- 9 in all; the demo has an automated test for 8 of them
ICR-AB-0002: Calendar synchronisation
Asynchronous eventVersion 0.11.0Status: proposed
How a copy of each staff member's busy times is kept current from Google Calendar or Microsoft 365, and bookings are written back.
- Follows the pattern
- Integration Native Connectors (Cloud)
- Cites
- ADR-AB-0003, ADR-AB-0004
- Acceptance criteria
- 8 in all; the demo has an automated test for 7 of them
ICR-AB-0003: Notification delivery
Asynchronous messageVersion 1.1.0Status: agreed
How booking events become confirmation and reminder messages handed to an email and SMS provider, exactly once.
- Follows the pattern
- Integration Middleware Services (Cloud)
- Cites
- ADR-AB-0006, ADR-AB-0007
- Acceptance criteria
- 6 in all; the demo has an automated test for 5 of them
Running demo: automated tests for the contracts
The demo is a working version of the system: a widget in the fictional clinic's web page and, behind it, a real API, a calendar connector and a notification worker, run on your own computer against stand-in providers. Nothing is sent anywhere. Its point is that a contract is not just a document.
What it shows
- 20 of the 23 acceptance criteria in the three contracts have an automated test, and a conformance test fails if a criterion loses its test.
- Fifty simultaneous confirmations of one slot produce exactly one booking and 49 conflict responses that list alternatives.
- The database itself refuses an overlapping booking when a row is inserted directly, bypassing the application.
- With the message provider down, bookings still succeed, and when it returns every pending message is sent exactly once.
What building it found
Seven places where the records were silent or wrong, for example no way to extend or release a hold, and a retry window counted from the wrong moment. All seven were fed back into the records; most are now checked by an acceptance criterion with an automated test. That loop is what the dashed arrows in the diagram show.
Where it differs from the design
- It uses SQLite with an overlap trigger where the design uses PostgreSQL. Tenant isolation is by code, not by the database's row-level security, which is weaker; its tests exercise the code, not the database.
- Events travel through an in-process log, not a message broker, and the calendar and message providers are stand-ins.
- Search latency (p95 under 400 ms) is measured on an empty server, not under load; the load test is still to be built, so the test shows the algorithm is fast, not that production load is met.
- 3 criteria, which check the contracts' examples against a specification file (OpenAPI, AsyncAPI, or a schema not yet written), are not automated, because the demo has none.
- Accessibility is checked with an automated checker and by keyboard; a check with a real screen reader is still owed.
To run it, clone the appointment-booking-demo repository. node tools/serve.mjs runs the in-browser version and needs only Node; npm run demo runs the real API and needs Node 22.13 or later. The repository's README has the conformance table, criterion by criterion.
Templates behind this example
- Architecture Decision Record (ADR) Template: its worked example is one of the records above
- Solution Architecture Design (SAD) Template: its worked example is one of the records above
- Reference architecture template: its worked example is the Integration reference architecture the design aligns to (section 4.5)
All of the templates are free to copy; Tools & Templates has the full list.