One FK-constrained join table per relationship type
(CompanyUser, TeamUser, CollectionTeam, ...)
Real DB integrity, but:
schema proliferation - every new relationship = new
table/migration/API/service/tests
repeated APIs doing the same conceptual thing
("relate A to B with these scopes")
risk of authorization logic drifting between relationship
implementations over time
Nothing stops a future contributor from bypassing a central service and
querying/adding a raw table directly - the "one abstraction" is a
convention, not a constraint
One permission_grant table: subject, resource, one scope per row,
soft-deleted (real audit trail - see below)
Governed by a typed relationship model (which subject/resource pairs
are legal, which scopes are valid) - not an untyped bag of IDs
GrantService is the only thing that ever touches the table -
two narrow methods (canPerform, getAccessQuery) are the entire
surface every other service gets
This is a hard line, not a soft one - there's no raw table left to
reach for if someone wants to bypass it
Why This Matters for a Small Team That Doesn't Know How It'll Grow
Yew is young - the actual shape of future relationships isn't known
today
Committing to N dedicated tables optimizes for a stable domain model
this project doesn't have yet
One centralized, validated path is a stronger constraint than N
typed tables + good intentions - same "platform enforces it, not
team discipline" principle as Postgres/cookies/hash-routing, applied
to the schema itself
No database-enforced foreign keys on either side of a relationship
No automatic cascade deletes - has to be built by hand
The plan: every resource-owning service's delete path explicitly
calls into GrantService to purge anything referencing it - six known,
enumerable call sites, not an open-ended risk
More validation work moves into the application layer, concentrated in
one service instead of duplicated across many
Same shape as Postgres over MongoDB, cookies over JWT, hash routing
over History API: give up some flexibility/elegance in exchange for
the platform enforcing correctness instead of relying on people
remembering to do the right thing
One scope per grant row + soft delete also means every grant and
revocation is independently auditable - a side effect that happens to
set up V4's audit-log requirement for free