Skip to main content
Background Image

Association Resources - Summary

·535 words·3 mins

Motivation
#

Many-to-many relationships (e.g., Users ↔ Groups) are common and sometimes carry metadata about the relationship itself (join time, role, etc.). Exposing the join-table rows as first-class association resources gives consumers the ability to address an individual relationship and store metadata about it.

Overview
#

  • The canonical storage for many-to-many relationships is a join table.
  • Expose that join table as a resource (e.g., Membership or UserGroup) so the API surface mirrors the schema: User, Group, Membership.
  • Association resources are created/deleted like any other resource (create = join, delete = leave).
  • Listing and filtering the association collection reveals relationships; retrieving a single association returns metadata about that relationship.

Implementation (key decisions)
#

Naming
#

  • Choose a meaningful name in context: CourseEnrollment, CourseMembership, Membership, Association, etc.

Standard methods
#

  • Implement standard resource methods: Create, Get, Update, Delete, List.
  • If the association holds metadata, support Get and Update. List is needed for browsing.

Uniqueness
#

  • Enforce uniqueness: there should be only one association resource for the same pair of resources (e.g., one membership per (userId, groupId)).
  • Attempts to create a duplicate association should fail (e.g., 409 Conflict).

Read-only fields
#

  • The pointer fields that identify the two resources (e.g., userId, groupId) should be treated as output-only for updates.
  • To change an association’s endpoints, delete the old association and create a new one. Update requests that attempt to change the cross-reference fields should ignore them (or error depending on policy).

Association alias methods
#

  • Provide convenience alias methods for common filtered queries, e.g.:

    • ListUserGroups({ userId }) — groups for a user (alias for ListMemberships?filter=userId:...)
    • ListGroupUsers({ groupId }) — users in a group (alias for ListMemberships?filter=groupId:...)
  • Naming convention: <OwningResource><ListedPlural>UserGroups = groups owned by a user; GroupUsers = users of a group.

Referential integrity
#

  • Common DB behaviors: Cascade, Restrict, Set null, Do nothing.

  • For association resources, prefer either:

    • Restrict — prevent deleting a resource while associations exist (return 412 Precondition Failed), or
    • Do nothing — allow deletion and leave dangling associations for the consumer to handle.
  • Avoid automatic cascade or bulk nulling due to risk of massive writes or delete avalanches.

Final API (example)
#

  • Top-level resources: User, Group, Membership.
  • Example endpoints/methods:
POST   /memberships                 -> CreateMembership
GET    /memberships/{id}            -> GetMembership
PATCH  /memberships/{id}            -> UpdateMembership
DELETE /memberships/{id}            -> DeleteMembership
GET    /memberships                 -> ListMemberships
GET    /groups/{groupId}/users      -> ListGroupUsers (alias)
GET    /users/{userId}/groups       -> ListUserGroups (alias)
  • Membership model stores userId, groupId, plus metadata fields like role, expireTime, etc.

Trade-offs
#

  • Flexibility: association resources are the most flexible representation (store relationship metadata, separate lifecycle).
  • Complexity: adds API surface (extra resource + methods) and cognitive overhead (create membership vs. JoinGroup).
  • Separation of concerns: group description and membership list are retrieved separately (may feel split but is practical for large lists).

Exercises (brief)
#

  1. Design an API associating users with chat rooms where role and join_date are stored on the Membership.
  2. To retain join/leave history while preventing concurrent duplicate presences, give each Membership a unique id and an isActive/leaveDate field; on join, ensure no active membership for (userId, roomId) exists.

Takeaway bullets
#

  • Association resources model many-to-many relationships and relationship-specific metadata.
  • They require special handling: uniqueness constraints, read-only cross-reference fields, and considered referential-integrity policies.
  • Alias listing methods improve ergonomics for common queries (/users/{id}/groups, /groups/{id}/users).