Keeping six unrelated booking verticals on one schema without either duplicating the platform six times or collapsing them into an unusable generic abstraction. A hotel booking, a taxi ride, a parcel, a bus seat, a rental and an errand have different pricing inputs, state machines, cancellation rules and parties.
What I did about it
Each vertical got its own tables and its own state machine — bookings, rides, parcel_shipments, bus_bookings, car_rental_bookings, errand_tasks — each first-class, each with its own pricing rules.
Unification happens only where the business genuinely is unified: transactions, wallets, commission_rules, users, notifications and support_tickets are shared across all six.
A global_bookings projection sits over the six for the admin console's cross-vertical views, so the UI never needs a switch statement per column.
Domain-package organisation reinforces the boundary — the code layout makes it obvious when someone is about to reach across a vertical they shouldn't.
What I rejected, and why
The cost is real: 186 tables, and adding a seventh vertical is genuine work rather than a config row. The generic type column plus JSON blob alternative has been tried repeatedly in the industry and produces a system where 'why did this booking charge the wrong commission' is unanswerable. Here commission logic lives in one shared place with per-vertical overrides, pinned by a test.
The coverage gate sits at 50% across a 186-table schema. That number is honest but low for a system moving commission payments, and I would raise the gate on the money and commission modules specifically rather than averaging across six verticals.