Multi-tenant Calendar SaaS
Lead Developer
Self-hosted πλατφόρμα κρατήσεων: ένα script tag σε οποιοδήποτε website και έχεις διαθεσιμότητα, calendar sync και επιβεβαιώσεις. Αντικαθιστά το Calendly και παρόμοια SaaS για ιατρεία, κομμωτήρια και service providers.
Το σύστημα
Το Multi-tenant Calendar SaaS είναι ένα self-hosted booking system που έχτισα ώστε ιατρεία, κομμωτήρια και service providers να μπορούν να ρίξουν ένα μόνο script tag στο site τους και να έχουν κρατήσεις ραντεβού, calendar sync και επιβεβαιώσεις, χωρίς να πληρώνουν Calendly ανά χρήστη τον μήνα. Ο πυρήνας είναι ένα Fastify 5 API πάνω σε PostgreSQL 16 + Drizzle + Redis/BullMQ. Η row-level multi-tenancy επιβάλλεται από έναν query wrapper που scope-άρει αυτόματα κάθε κλήση στη βάση με tenant_id, ώστε ένας tenant να μην μπορεί να διαβάσει κατά λάθος τα δεδομένα άλλου, γιατί το επίπεδο της βάσης το αποτρέπει, όχι επειδή το θυμήθηκε ο developer. Το admin SPA είναι React 19 + TanStack με Better Auth. Το embed widget είναι vanilla JS, ένα μόνο <script> tag το ρίχνει σε οποιοδήποτε site τρίτου. OAuth calendar sync (Google + Outlook) με DST-safe availability windows χειρίζεται τις cross-timezone κρατήσεις. Ένας async worker αναλαμβάνει confirmation emails και hold expiry. 103 TypeScript αρχεία, 96 test αρχεία, deployed μέσω Coolify.
Αρχιτεκτονική
- API: Fastify 5 (Node 22), Postgres 16 + Drizzle ORM, Redis + BullMQ, Zod, date-fns/tz
- Widget: Vanilla JS bundle (drop-in
<script>tag στους πελάτες των tenants) - Admin SPA: React 19 + TanStack Router/Query/Table, Better Auth
- Worker: Separate process για async jobs (confirmation emails, hold expiry, calendar sync)
- Multi-tenancy: Row-level με
tenant_idσε όλα τα data tables, query wrapper enforcing isolation, prefixed nanoid IDs για human-readable logs (tenant_abc123) - Calendar sync: Google OAuth + Outlook OAuth per tenant provider, με DST-safe availability windows
- Per-tenant config: SMTP credentials, locale, timezone, branding
- Deployment: Coolify (webhook-triggered) με multi-stage Docker build
Η συνεισφορά μου
Σχεδίασα και έχτισα το σύστημα μόνος μου. Η αρχιτεκτονική απόφαση για την οποία είμαι πιο περήφανος είναι η στρατηγική row-level multi-tenancy: κάθε query στη βάση περνάει από έναν wrapper που inject-άρει αυτόματα WHERE tenant_id = ?, οπότε ο developer δεν χρειάζεται να το θυμάται και ένα tenant data leak γίνεται δομικά αδύνατο. Τα prefixed nanoid IDs (tenant_abc123, booking_xyz789) δίνουν στους support engineers αναγνώσιμα από άνθρωπο logs, χωρίς την ασάφεια των UUIDs. Έγραψα το vanilla JS embed widget από το μηδέν, γιατί οι tenants το βάζουν σε sites τρίτων όπου δεν μπορώ να υποθέσω ότι θα υπάρχει React ή Vue. Το OAuth calendar sync (Google + Outlook) το υλοποίησα ανά provider με DST-safe availability windows μέσω date-fns/tz, και ένας BullMQ worker χειρίζεται confirmation emails και sync jobs, χωρίς να μπλοκάρει το HTTP request path. Τα 96 test αρχεία χωρίζονται σε unit, integration, e2e, και Playwright UI tests για το admin, ώστε να υπάρχει γρήγορος feedback loop στο CI.
Stack λεπτομέρειες
Τα 96 test αρχεία χωρίζονται σε unit (isolated logic), integration (DB + Redis), e2e (full API flows με Vitest), και admin (Playwright UI tests). Αυτό το split επιτρέπει fast feedback loop: unit tests σε milliseconds, integration tests με real DB (Testcontainers), e2e τελευταία. Ο query wrapper για tenant isolation είναι ένα Drizzle middleware που wraps κάθε db.select() / db.insert() / db.update() / db.delete(), εξαιρούνται μόνο explicit super-admin queries. Τα prefixed nanoid IDs λύνουν πρακτικό πρόβλημα: όταν ο support διαβάζει logs, booking_7kRt3m είναι αμέσως αναγνωρίσιμο ως booking ID. Το DST-safe availability: κάθε provider αποθηκεύει timezone string (Europe/Athens), και οι slot calculations γίνονται με date-fns/tz για ακριβή DST transitions, αποφεύγοντας τον κλασικό bug των double-booking σε αλλαγή ώρας.
Αποτελέσματα
- Παραγωγικό multi-tenant booking system: έτοιμο για onboarding πραγματικών πελατών
- Εκτεταμένο test coverage (96 αρχεία) μεγαλώνει την εμπιστοσύνη για μελλοντικές αλλαγές
- Documented architecture (
docs/superpowers/specs/) μειώνει onboarding χρόνο για future contributors - Production deployment μέσω Coolify με webhook deploys
Πρόκληση
Η δυσκολότερη πτυχή ήταν η DST-safe availability λογική με cross-tenant timezones. Κάθε provider έχει δικό του timezone. Ένας guest σε Europe/London κάνει booking για έναν provider σε Europe/Athens. Αν η αλλαγή ώρας γίνεται ανάμεσα στο booking creation και το appointment, ο slot μπορεί να εμφανίζεται wrong σε έναν από τους δύο. Η λύση: αποθήκευσα όλα τα slots σε UTC, αλλά η display και validation λογική χρησιμοποιεί date-fns/tz με το provider timezone, όχι το guest timezone. Το availability window calculation (ποιες ώρες είναι available) γίνεται πάντα στο provider timezone, μετατρέπεται σε UTC για storage, και ξαναμετατρέπεται για display. Το test suite για αυτό περιλαμβάνει explicit DST transition dates για Europe/Athens και Europe/London ώστε να ελέγχω edge cases.
Σύνδεσμοι
- Repo:
jimrarras/multi-tenant-calendar(private)
Παρόμοια έργα
- TaskFlow: άλλο multi-tenant SaaS με real-time και offline-first, αλλά για task dispatch αντί για bookings
- Heartbeat Pharmacy PIM: άλλο production system με Better Auth και Coolify deployment
Στιγμιότυπα