v1 spine — conceptual ER
The three primary entities of the v1 product spine and their relationships. This is a conceptual diagram — it shows domain relationships, not exact database schema column names.
<!-- Member entity -->
<div style="border:2px solid var(--docs-secondary);border-radius:10px;overflow:hidden">
<div style="background:var(--docs-secondary);padding:8px 14px">
<div style="font-weight:700;color:#ffffff;font-size:0.9rem">Member</div>
<div style="color:color-mix(in srgb,#ffffff 75%,transparent);font-size:0.7rem">People context</div>
</div>
<div style="padding:10px 14px;background:color-mix(in srgb,var(--docs-secondary) 8%,#ffffff)">
<div style="display:flex;flex-direction:column;gap:4px">
<div style="display:flex;gap:8px;align-items:baseline">
<span style="font-size:0.65rem;font-weight:700;color:var(--docs-secondary);min-width:18px">PK</span>
<span style="font-size:0.78rem;color:var(--docs-primary);font-family:monospace">id</span>
</div>
<div style="height:1px;background:var(--docs-border);margin:2px 0"></div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">name</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">email</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">phone?</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">roles[ ]</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">status</div>
</div>
</div>
</div>
<!-- Booking entity (centre) -->
<div style="margin:0 -2px;position:relative;z-index:1">
<div style="border:2px solid #f97316;border-radius:10px;overflow:hidden;box-shadow:0 2px 12px color-mix(in srgb,#f97316 20%,transparent)">
<div style="background:#f97316;padding:8px 14px">
<div style="font-weight:700;color:#ffffff;font-size:0.9rem">Booking</div>
<div style="color:color-mix(in srgb,#ffffff 75%,transparent);font-size:0.7rem">Scheduling context</div>
</div>
<div style="padding:10px 14px;background:color-mix(in srgb,#f97316 6%,#ffffff)">
<div style="display:flex;flex-direction:column;gap:4px">
<div style="display:flex;gap:8px;align-items:baseline">
<span style="font-size:0.65rem;font-weight:700;color:#f97316;min-width:18px">PK</span>
<span style="font-size:0.78rem;color:var(--docs-primary);font-family:monospace">id</span>
</div>
<div style="height:1px;background:var(--docs-border);margin:2px 0"></div>
<div style="display:flex;gap:8px;align-items:baseline">
<span style="font-size:0.65rem;font-weight:700;color:var(--docs-secondary);min-width:18px">FK</span>
<span style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">memberId</span>
</div>
<div style="display:flex;gap:8px;align-items:baseline">
<span style="font-size:0.65rem;font-weight:700;color:var(--docs-secondary);min-width:18px">FK</span>
<span style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">aircraftId</span>
</div>
<div style="display:flex;gap:8px;align-items:baseline">
<span style="font-size:0.65rem;font-weight:700;color:color-mix(in srgb,var(--docs-secondary) 60%,#ffffff);min-width:18px">FK?</span>
<span style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 55%,transparent);font-family:monospace">instructorId</span>
</div>
<div style="height:1px;background:var(--docs-border);margin:2px 0"></div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">startAt</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">endAt</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">status</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 55%,transparent);font-family:monospace">cancelledAt?</div>
</div>
</div>
</div>
</div>
<!-- Aircraft entity -->
<div style="border:2px solid #22c55e;border-radius:10px;overflow:hidden">
<div style="background:#22c55e;padding:8px 14px">
<div style="font-weight:700;color:#ffffff;font-size:0.9rem">Aircraft</div>
<div style="color:color-mix(in srgb,#ffffff 75%,transparent);font-size:0.7rem">Aircraft context</div>
</div>
<div style="padding:10px 14px;background:color-mix(in srgb,#22c55e 6%,#ffffff)">
<div style="display:flex;flex-direction:column;gap:4px">
<div style="display:flex;gap:8px;align-items:baseline">
<span style="font-size:0.65rem;font-weight:700;color:#22c55e;min-width:18px">PK</span>
<span style="font-size:0.78rem;color:var(--docs-primary);font-family:monospace">id</span>
</div>
<div style="height:1px;background:var(--docs-border);margin:2px 0"></div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">registration</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">type</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 70%,transparent);font-family:monospace">status</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 55%,transparent);font-family:monospace">groundedReason?</div>
<div style="font-size:0.75rem;color:color-mix(in srgb,var(--docs-primary) 55%,transparent);font-family:monospace">groundedAt?</div>
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px;padding-top:2px">
<div style="width:1px;height:20px;background:var(--docs-border)"></div>
<div style="font-size:0.65rem;color:color-mix(in srgb,var(--docs-primary) 50%,transparent);text-align:center">1 Member<br>has many Bookings</div>
<div style="display:flex;align-items:center;gap:4px;width:100%">
<div style="flex:1;height:1px;background:var(--docs-border)"></div>
<div style="font-size:0.65rem;padding:2px 6px;border:1px solid var(--docs-border);border-radius:4px;color:color-mix(in srgb,var(--docs-primary) 45%,transparent);background:var(--docs-surface-2);white-space:nowrap">1 : N</div>
<div style="flex:1;height:1px;background:var(--docs-border)"></div>
</div>
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px;padding-top:2px">
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px;padding-top:2px">
<div style="width:1px;height:20px;background:var(--docs-border)"></div>
<div style="font-size:0.65rem;color:color-mix(in srgb,var(--docs-primary) 50%,transparent);text-align:center">1 Aircraft<br>has many Bookings</div>
<div style="display:flex;align-items:center;gap:4px;width:100%">
<div style="flex:1;height:1px;background:var(--docs-border)"></div>
<div style="font-size:0.65rem;padding:2px 6px;border:1px solid var(--docs-border);border-radius:4px;color:color-mix(in srgb,var(--docs-primary) 45%,transparent);background:var(--docs-surface-2);white-space:nowrap">1 : N</div>
<div style="flex:1;height:1px;background:var(--docs-border)"></div>
</div>
</div>
Domain rules enforced at booking time
🚫 Rejected
- Aircraft is grounded
- Aircraft already booked in overlapping time range
- Instructor already booked in overlapping time range
- Member is inactive
✓ Accepted when
- Aircraft status is available
- No confirmed bookings overlap the time range
- Instructor (if assigned) is free in the time range
- Booking owner is an active Member
See Scheduling bounded context for the full domain rules.