ArchitectureData Model

Data Model

Persisted entities, relationships, uniqueness, and database deletion rules.

Status: complete · Source: content/docs/architecture/data-model.mdx

Prisma models the backend's PostgreSQL data in backend/prisma/schema.prisma. The eight models describe accounts, clubs, events and registrations, mentors and likes, blogs, and testimonials. Controllers query these models directly; the relationships and constraints below shape what those controllers can persist.

Persisted entities

ModelDomain role
UserAccount identity, password/Google profile, role, and optional club membership.
ClubCommunity club with members, events, mentors, and a separately stored leadership identifier.
EventScheduled event belonging to one club.
EventRegistrationA user's registration for one event, with a registration timestamp.
MentorMentor profile belonging to one club; it is not linked to a user account.
MentorLikeA user's like for one mentor.
BlogAuthored content with DRAFT, PUBLISHED, or ARCHIVED status; new records default to DRAFT.
TestimonialStandalone name, batch year, content, and optional image; no user or club relation.

All models have string primary identifiers generated by Prisma's cuid() default. @@map maps them to PostgreSQL tables such as users, event_registrations, and mentor_likes; the SQL migrations define their text primary-key columns without a database-side CUID default.

Relationships

Loading diagram…

Diagram source
erDiagram
    accTitle: ElixirV4 persisted relationships
    accDescr: Club membership, events and registrations, mentor likes, blog authorship, and standalone testimonials.
    Club |o..o{ User : membership
    Club ||..o{ Event : hosts
    Club ||..o{ Mentor : groups
    User ||..o{ EventRegistration : registers
    Event ||..o{ EventRegistration : receives
    User ||..o{ Blog : authors
    User ||..o{ MentorLike : likes
    Mentor ||..o{ MentorLike : receives
    User {
        string id PK
        string clubId FK "optional"
    }
    Club {
        string id PK
        string clubHeadId UK "optional, not a foreign key"
    }
    Event {
        string id PK
        string clubId FK
    }
    EventRegistration {
        string id PK
        string userId FK
        string eventId FK
    }
    Mentor {
        string id PK
        string clubId FK
    }
    MentorLike {
        string id PK
        string userId FK
        string mentorId FK
    }
    Blog {
        string id PK
        string authorId FK
    }
    Testimonial {
        string id PK
    }

A user belongs to zero or one club through nullable User.clubId; a club can have many users. Every event and mentor requires one club. Every blog requires one author. Registrations and likes are join records with their own identifiers: each requires one user and one event or mentor, respectively. Parents can have zero related records.

The diagram deliberately has no leadership edge for Club.clubHeadId: it is a unique scalar, not a Prisma relation or SQL foreign key. The pair constraints on registrations and likes are described below, rather than drawn as single-column uniqueness.

Sourcebackend/prisma/schema.prisma

Constraints that affect behavior

User email and a non-null Google subject (googleId) are unique. Google controllers locate users by email, and the unique subject prevents assigning the same non-null Google identifier to multiple accounts. Club names and non-null clubHeadId values are also unique. Nullable unique columns can contain multiple null values in PostgreSQL.

EventRegistration has a compound unique constraint on (userId, eventId), so the database permits at most one registration per user/event pair. MentorLike has the equivalent constraint on (userId, mentorId). Controller lookups check these pairs, but the database constraint remains the final boundary if concurrent requests attempt duplicates.

Foreign keys enforce the existence of referenced clubs, users, events, and mentors. Required relationships cannot be set to null. A mentor's identity is independent of User; sharing a name does not create an account relationship. Testimonials likewise store their attribution directly, without a relational link to an account.

User.role and Blog.status are persisted PostgreSQL enums. Their permitted values are database constraints, but rules about who may change them are implemented in routes/controllers. See Authentication & Authorization.

Sourcebackend/prisma/migrations/20250829203812_initial_migration/migration.sql

Sourcebackend/prisma/migrations/20251017102352_add_google_oauth_fields/migration.sql

Sourcebackend/prisma/migrations/20251025152715_add_mentor_likes/migration.sql

Deletion behavior

The checked-in SQL migrations establish these foreign-key actions. Later migrations do not replace them.

Deleted parentDatabase behavior for related records
ClubSets members' User.clubId to null; events and mentors referencing the club restrict deletion.
UserEvent registrations and authored blogs restrict deletion; mentor likes cascade.
EventRegistrations restrict deletion.
MentorMentor likes cascade.

RESTRICT prevents deletion while referencing rows remain. SET NULL preserves the child with its optional relationship cleared. CASCADE deletes the related rows when the parent is deleted successfully. All these foreign keys use ON UPDATE CASCADE for referenced identifiers.

The event-deletion controller explicitly removes registrations before deleting the event. These are separate awaited statements, not a transaction or a database cascade. The mentor-deletion controller deletes the mentor directly and relies on the like foreign key's cascade.

Club.clubHeadId has no foreign key, so deleting a user does not clear that scalar or prevent deletion on its own. Other restrictive relations may still block the user deletion.

Sourcebackend/prisma/migrations/20250901211052_add_blogs/migration.sql

Sourcebackend/src/controllers/userController.ts

Sourcebackend/src/controllers/eventController.ts

Club leadership and membership

Three independent pieces of state participate in club access:

StateWhat is enforced
User.roleMust be one of the role enum values; it does not imply club membership.
User.clubIdOptional foreign key to an existing club; at most one club per user.
Club.clubHeadIdOptional unique string; no foreign key to User and no role/membership constraint.

Club creation looks up a user by clubHeadEmail, creates a club with that user's ID as clubHeadId, then updates the user's role to CLUB_HEAD and membership to the new club. These writes are not in a transaction. Separately, user administration can change a role or assign, remove, or update membership without updating the club's leadership scalar.

Sourcebackend/src/controllers/clubController.ts

Migrations

Six checked-in Prisma migrations evolve the initial accounts/clubs/events/mentors schema with blogs, testimonials, mentor LinkedIn URLs, Google identity fields, and mentor likes. migration_lock.toml identifies PostgreSQL as the provider. The schema describes the current generated client model; migration SQL records the database changes, including deletion actions and unique indexes.

The backend's db:migrate script runs prisma migrate dev. Deployment does not automatically follow from the presence of migrations; see Deployment & Configuration. Use Change the Database Schema to plan, review, and verify a migration.

Sourcebackend/prisma/migrations/

Last updated · Edit this page on GitHub

On this page