Production-grade multi-tenant SaaS platform built with Next.js, Prisma, PostgreSQL, Redis, BullMQ, and TailAdmin.
Tenovo was built as a public architecture showcase demonstrating scalable SaaS engineering patterns including tenant isolation, RBAC, background job processing, audit logging, distributed systems concepts, and production-grade infrastructure.
- Next.js (App Router)
- React
- TypeScript
- TailwindCSS
- TailAdmin
- Axios
- Sonner
- Next.js Route Handlers
- Prisma 7
- PostgreSQL
- Redis
- BullMQ
- Auth.js (NextAuth v5)
- Docker
- PostgreSQL Container
- Redis Container
Tenovo uses a shared-database multi-tenant architecture.
Every record is tenant-scoped using:
organizationIdAll queries enforce tenant isolation.
Example:
await prisma.project.findMany({
where: {
organizationId: membership.organizationId,
},
});Supported roles:
OWNER
ADMIN
MEMBER
VIEWERPermissions are enforced both:
- Backend APIs
- Frontend UI
Examples:
- Only ADMIN/OWNER can manage team members
- OWNER cannot be removed
- OWNER role cannot be modified
The application uses a server-to-client tenant context architecture.
Server Layout
→ Fetch session + membership
→ Pass to AppProvider
→ Available globally in client componentsProvided context:
user
organization
organizations[]
roleOne user can belong to multiple organizations.
Active organization is stored using:
tenovo_active_org_idTenant switching updates:
- Projects
- Audit logs
- Dashboard metrics
- Team members
without mixing tenant data.
Implemented using Auth.js v5.
Features:
- Credentials authentication
- JWT sessions
- Protected admin routes
- Secure password hashing with bcryptjs
- Middleware-based route protection
Redis-backed BullMQ queues are used for asynchronous processing.
Current implementation:
Organization invitation email jobsArchitecture:
API Route
→ Queue Producer
→ Redis Queue
→ Worker ProcessThis pattern is extensible for:
- Emails
- AI processing
- Video processing
- Webhooks
- Report generation
- Scheduled jobs
All critical actions are tracked.
Implemented events:
project.created
membership.created
membership.role_updated
membership.removedAudit logs are tenant-scoped and queryable from the dashboard.
- Sign up
- Sign in
- Sign out
- Protected routes
- Session handling
- Multi-tenant organizations
- Organization switching
- Membership management
- Tenant-scoped CRUD
- Modal creation flow
- Toast notifications
- Realtime project-created notifications
- Add members
- Remove members
- Change roles
- RBAC enforcement
- Activity history
- Tenant-scoped event tracking
- Server-rendered jobs dashboard
- BullMQ queue statistics
- Recent job visibility
- Waiting / active / completed / failed / delayed job counts
- Redis queues
- BullMQ workers
- Async job processing
- Dedicated Socket.IO realtime service
- Redis Pub/Sub notification broadcasting
- Server-rendered queue/job monitoring dashboard
The Prisma schema is split into separate files.
prisma/
schema.prisma
enums/
role.prisma
models/
user.prisma
account.prisma
session.prisma
verification-token.prisma
organization.prisma
membership.prisma
project.prisma
audit-log.prismaOne model per file.
One enum per file.
Application-level types are separated into dedicated reusable files.
src/types/Examples:
src/types/email-job-name.ts
src/types/organization-role.ts
src/types/api-response.tsThis keeps:
- Queue systems strongly typed
- API contracts reusable
- Shared frontend/backend types centralized
- Business logic easier to maintain
Configured using Prisma 7:
prisma.config.tssrc/
app/
(admin)/
(full-width-pages)/
api/
context/
lib/
queues/
workers/
types/
prisma/Services:
- PostgreSQL
- Redis
Run locally:
docker compose up -dTenovo uses a production-grade containerized deployment architecture with GitHub Actions CI/CD, Docker Compose, PostgreSQL, Redis, and host-level Nginx reverse proxying.
Deployments are triggered automatically whenever the:
deploybranch is updated.
GitHub (deploy branch)
↓
GitHub Actions
↓
SSH/SCP deployment
↓
Ubuntu VPS
↓
Docker Compose Stack
├── Next.js App
├── PostgreSQL
├── Redis
└── Prisma Migration Runner
↓
Host Nginx Reverse Proxy
↓
HTTPS DomainThe VPS host only manages:
- Docker Engine
- Docker Compose
- Nginx
- Certbot SSL
No application runtime or Node.js dependencies are installed directly on the host machine.
This keeps deployments reproducible, isolated, and easy to maintain.
Each application runs in an isolated Docker Compose stack.
Current Tenovo services:
| Service | Purpose |
|---|---|
| app | Next.js production runtime |
| realtime | Dedicated Socket.IO realtime server |
| worker_email | BullMQ background email worker |
| postgres | PostgreSQL database |
| redis | Redis cache / BullMQ backend / PubSub |
| migrate | Dedicated Prisma migration runner |
Nginx runs directly on the host VPS and proxies traffic to internal Docker services.
Example:
https://tenovo.example.com
↓
127.0.0.1:3100
↓
Docker container :3000This architecture allows multiple independent applications to coexist safely on the same VPS.
Deployment is Git-driven.
Workflow:
- Push to
deploy - GitHub Actions workflow starts
- Repository checked out
- Production environment generated from GitHub Secrets
- Files uploaded to VPS via SCP
- Docker Compose rebuilds containers
- Prisma migrations execute
- Old Docker images pruned
Primary workflow:
.github/workflows/deploy.ymlSensitive values are never committed to source control.
Secrets are stored using:
GitHub Repository SecretsCurrent deployment secrets:
| Secret | Purpose |
|---|---|
| SERVER_HOST | VPS hostname/IP |
| SERVER_USER | SSH deployment user |
| SERVER_SSH_KEY | Private SSH deployment key |
| SERVER_APP_DIR | Remote deployment directory |
| PRODUCTION_ENV | Full production environment file |
The repository only includes:
.env.production.exampleReal production values are securely injected during deployment through GitHub Actions.
Containers use multi-stage Docker builds.
Key characteristics:
- Node.js Alpine runtime
- Next.js standalone output
- Small production images
- Internal-only service networking
- Isolated runtime environments
- Independent worker scaling
- Separate realtime infrastructure
Realtime communication uses a dedicated Socket.IO server container.
Architecture:
Next.js App
↓
Redis Pub/Sub
↓
Socket.IO Realtime Server
↓
Connected ClientsThis architecture avoids coupling websocket infrastructure directly into the Next.js runtime.
Benefits:
- Independent scaling
- Cleaner separation of concerns
- Better production deployment compatibility
- Redis-backed horizontal scaling support
BullMQ workers run as dedicated isolated containers.
Current workers:
worker_emailThis architecture supports future expansion into:
- AI processing workers
- Video transcoding workers
- Webhook processing workers
- Scheduled jobs
- Analytics pipelines
Database migrations run using a dedicated migration service:
docker compose run --rm migrateThis keeps Prisma tooling out of the lightweight application runtime container.
PostgreSQL and Redis are not publicly exposed.
Only the internal application port is bound:
127.0.0.1:3100External traffic must pass through Nginx.
Deployments use SSH key authentication.
No passwords are used.
Credentials, production paths, and environment variables are never committed to the repository.
Deployment can also be executed manually on the VPS:
docker compose --env-file .env.production -f docker-compose.prod.yml up -d --build
docker compose --env-file .env.production -f docker-compose.prod.yml run --rm migratenpm installdocker compose up -dnpx prisma migrate devnpx prisma generatenpm run devnpm run worker:emailMost of my production work was built inside private company repositories and cannot be shared publicly.
Tenovo was created to publicly demonstrate:
- Multi-tenant SaaS architecture
- Distributed systems thinking
- Real-world backend engineering
- Modern Next.js architecture
- Prisma/PostgreSQL design
- Redis queue systems
- RBAC implementation
- Production-grade infrastructure patterns
Planned improvements:
- Real email provider integration
- Queue retry dashboard
- WebSocket notification center
- Billing architecture
- AI task processing queues
- Activity feed
- File uploads
- S3/Wasabi integration
- Rate limiting
- Monitoring/observability
- CI/CD pipeline improvements
- Kubernetes deployment
- Blue/green deployments
- Centralized logging
- Automated backups
- Distributed websocket scaling
This project is intentionally focused on:
Architecture > tutorial CRUDThe goal is to demonstrate:
- Scalable engineering patterns
- Clean system design
- Production-ready thinking
- Full-stack product ownership
rather than isolated UI components.