Architecture judgment under changing constraints
Learning outcome and why the problem is difficult
An enrollment team asks for “a fast, reusable flow.” This does not select a framework. First discover who submits, whether submissions have legal or financial consequences, which partners embed the flow, how identities cross origins, and what must survive a failed connection. The outcome is a decision that another engineer can falsify, rather than an architecture diagram that merely looks plausible.
Mental model: invariants before boundaries
Separate six boundaries: business capability, UI ownership, network, process lifetime, trust, and deployment. A React folder can enforce none of the last three by itself. A BFF may consolidate several network calls while still depending on a remote authority for identity. A reusable component can move to a package without granting its host permission to perform an operation.
For our synthetic enrollment scenario, preserve these invariants: only an authorized operator submits; accepted work has one stable receipt; retrying an unchanged operation does not create another record; changing an operation's payload cannot reuse its receipt. “Disable the button” improves UX but does not establish any of these server properties.
Worked decision and constraint change
| Option | Useful property | Cost or disqualifier |
|---|---|---|
| React app calling partner APIs | Few infrastructure pieces | Browser receives partner-specific contracts; credential handling may disqualify it |
| React plus BFF | Central authorization, validation and receipts | Adds service ownership and availability requirements |
| Hosted enrollment redirect | Central release and security boundary | Navigation and partner branding constraints |
Choose a BFF for the fixture because its operation ledger must be authoritative. Start with a modular monolith: a module and a database transaction are enough. Do not add a message broker until work must outlive the request or cross transaction boundaries.
Now change the requirement: the partner permits only an iframe and blocks third-party cookies. The original same-origin cookie design no longer transfers. Evaluate a top-level redirect handshake or explicitly scoped short-lived integration credentials; do not weaken cookie policy as an architectural shortcut. Record the changed assumption and rerun the identity journey. This is an example decision, not an OAuth implementation in the lab.
Failure investigation and verification
Enable “Lose the response” in the account portal. The server commits and the browser sees a network error. Diagnose from the persisted receipt and request key, not the text of the error toast. A second POST with the same key must recover the receipt. The HTTP integration test actually drops the response after committing.
Estimate load before distributing the service. If a hypothetical campaign has 12,000 visitors per minute and 4% submit, average writes are eight per second. Neither average traffic nor SQLite's local test throughput is evidence about peak production capacity. Measure burst factor, payload size, contention and partner latency before selecting infrastructure.
Exercise, expected answer and rollout
Write a one-page ADR with three options, two hard constraints, a reversible slice, and a kill condition. Inject the response loss and attach the assertion for one persisted write. Then change the partner constraint above. A strong answer revises identity delivery, preserves server idempotency, and distinguishes local evidence from production capacity. Roll out to one partner with a receipt lookup fallback; roll back the UI without deleting accepted operations.
Executable reference and sources
Implementation · Behavioral evidence · Primary source.
Run the projects and assessment. Human review is pending.