Problem & Solution
Problem
Blood-donation work fragments naturally: scheduling, donation records, unit inventory, and cross-hospital requests drift into disconnected processes, partial writes corrupt the lifecycle, and a single status field cannot represent several hospitals responding to one request.
Solution
Cruor unifies the lifecycle in one relational system — donor → appointment or campaign → donation → blood unit → inventory → inter-hospital request. Donation writes are transactional, inventory is derived from unit-level rows, and each hospital's offer is its own record. It ships as a modular monolith with a Next.js client; concurrency hardening is queued as future work.
Architecture
Recording a donation wraps the donation record, its blood unit, and the source appointment or registration update in a single transaction — the workflow can never persist a donation without its unit. A hospital's inventory is derived from its blood units rather than stored as a separate count.
Engineering Decisions
Transactional donations
Donation creation, blood-unit creation, and the source-state update commit in one transaction. A failure anywhere rolls everything back — no orphans, no unit-less records.
Units, not counters
Each physical unit is its own BloodUnit row; inventory views filter and group those rows. Availability reasons over persisted data, and every unit traces to its donation.
Independent offers
Each hospital's response is its own BloodRequestResponse row; ACCEPT is an offer, the requester selects one, allocation is a request state transition. Row-level locking is future hardening.
Challenges & Learnings
Two hospitals can accept the same request independently, so allocation had to become a state transition on the request instead of a field update. The lesson: model the conflict in the schema first — independent response rows plus request state — before reaching for locks. Reservation at accept-time remains queued as future work.
Limitations & next steps
- Offer-acceptance concurrency hardening
- Blood-unit expiry background processing
- Caching, workers, and notifications
- Observability and fuller automated testing

