Skip to main content

Database Schema

PostgreSQL 16+. All primary keys are UUID. All timestamps are stored in UTC.


Users & Auth

users

ColumnTypeDescription
idUUID (PK)
emailstringUnique
phonestringUnique
roleenumclient · professional · admin
encrypted_passwordstringbcrypt
confirmed_attimestampEmail confirmation
discarded_attimestampSoft delete

Salon owner is determined via salons.owner_id, not through role.

profiles

ColumnType
user_idUUID (FK → users)
first_namestring
last_namestring
avatar_urlstring
biotext
localestring

oauth_identities

ColumnTypeDescription
idUUID (PK)
user_idUUID (FK)→ users.id
providerstring"google" and others in the future
uidstringUnique ID at the provider (sub)
created_attimestamp
updated_attimestamp

Unique index: provider + uid.


Salons & Services

salons

ColumnTypeDescription
idUUID (PK)
owner_idUUID (FK)→ users.id
namestring
slugstringURL-friendly, unique
addressstring
lat, lngdecimalCoordinates
phonestring
statusenumpending · active · suspended
photo_urlstring
reviews_countintegerCounter cache
ratingdecimalAverage rating
currencystringISO 4217, default MDL
timezonestringIANA, default Europe/Chisinau
cancellation_hours_beforeintegerDefault 24
cancellation_fee_percentdecimalDefault 0
auto_cancel_after_minintegernullable

salon_services (AR model: Service)

ColumnType
idUUID (PK)
salon_idUUID (FK)
namestring
descriptiontext
duration_mininteger
category_idinteger (FK → categories)

Unique index: salon_id + name.

service_master_prices (AR model: ServiceMasterPrice)

ColumnType
idUUID (PK)
salon_idUUID (FK)
service_idUUID (FK → salon_services)
master_idUUID (FK → users)
pricedecimal

Unique index: service_id + master_id.

categories

ColumnType
idinteger
namestring
slugstring
parent_idinteger (self-join, nullable)
positioninteger

Memberships & Master Profiles

salon_memberships

ColumnTypeDescription
idUUID (PK)
salon_idUUID (FK)
user_idUUID (FK)
roleenummaster · receptionist
statusenumpending · active · deactivated
invite_tokenstringFor accepting invitations
invited_attimestamp
accepted_attimestampnullable

Unique index: salon_id + user_id.

master_profiles

ColumnType
idUUID (PK)
user_idUUID (FK)
specializationstring
experience_yearsinteger
instagram_urlstring
portfolio_urlstring
reviews_countinteger
ratingdecimal

Scheduling & Bookings

working_hours

ColumnTypeDescription
salon_idUUID (FK)
membership_idUUID (FK → salon_memberships, nullable)null = entire salon
day_of_weekinteger0=Sun, 1=Mon ... 6=Sat
start_timetime
end_timetime

appointments

ColumnTypeDescription
idUUID (PK)
client_idUUID (FK)→ users.id
salon_idUUID (FK)
master_idUUID (FK)→ users.id
service_idUUID (FK)→ salon_services.id
starts_attimestamp
ends_attimestamp
statusstringAASM: confirmed/in_progress/completed/reviewed/cancelled/no_show
price_snapshotdecimalPrice at the time of booking (frozen)
duration_min_snapshotintegerDuration at the time of booking
total_amountdecimal

Reviews & Favorites

reviews

ColumnType
idUUID (PK)
appointment_idUUID (FK)
client_idUUID (FK)
salon_idUUID (FK)
master_idUUID (FK)
ratinginteger
bodytext

favorites

ColumnType
idUUID (PK)
user_idUUID (FK)
salon_idUUID (FK)

Unique index: user_id + salon_id.