$ cat ~/projects/smartcampus.mdx
SmartCampus.co.id
Multi-tenant backend architecture supporting multiple universities under a unified SmartCampus ecosystem.
- Next.js
- Go
- MySQL
Context
SmartCampus wanted a single platform that multiple universities could share, with each university feeling like it had its own instance. The product surface was wide — auth, course delivery, content management, payments — and the team was small enough that I was touching most of it.
What I built
- Multi-tenant backend in Go. Every request carries a tenant context (resolved from the subdomain), and every database query is scoped by
tenant_id. The schema is shared across tenants but logically partitioned via thetenant_idcolumn on every table; we considered schema-per-tenant early on and decided against it because migrations across N schemas were going to be a nightmare. - Authentication with JWT + refresh tokens, role-based access (admin, lecturer, student), and tenant-scoped permissions. The auth service is the only one that knows about password hashes; everything else trusts the JWT claims.
- Schema design for institutions, users, courses, enrollments, learning modules, content blocks, and resource metadata. Every foreign key has a
tenant_idcolumn to make "show me everything for this university" a single index lookup. - Next.js frontend for the student and lecturer dashboards, with the Go backend serving a JSON API.
Outcome
Three universities were onboarded on the same codebase without any of them seeing each other's data. Adding a fourth took about two weeks — most of which was content migration, not code changes.
What I'd do differently: the multi-tenant query pattern is great until someone forgets the WHERE tenant_id = ? clause. I'd add a per-tenant test fixture and a lint rule (or a query wrapper that makes tenant scoping mandatory) from the start. We caught the omissions in code review, but a belt-and-suspenders approach would have been safer.