The Challenge
A growing property management company approached us with a spreadsheet-based operation serving 200 properties. They needed a cloud platform that could manage tenant records, automate rent collection, handle maintenance requests, and scale to thousands of properties.
Key requirements:
- Multi-tenant architecture (each property company gets isolated data)
- Real-time dashboards for property managers
- Automated payment processing and reminders
- Mobile-first tenant portal
- Role-based access control
- Audit trail for all financial operations
Architecture Decisions
Multi-Tenancy Strategy
We evaluated three approaches:
| Strategy | Isolation | Cost | Complexity |
|---|---|---|---|
| Shared database, shared schema | Low | Low | Low |
| Shared database, separate schemas | Medium | Medium | Medium |
| Separate databases per tenant | High | High | High |
We chose shared database with row-level security using PostgreSQL's RLS policies. This gives us strong data isolation without the operational overhead of managing hundreds of databases.
Technology Stack
- Frontend: Next.js 14 with React Server Components
- Backend: NestJS with Fastify adapter
- Database: PostgreSQL with Prisma ORM
- Queue: BullMQ with Redis for async processing
- Auth: JWT with refresh token rotation
- Payments: Stripe Connect for multi-party payments
- Hosting: AWS (ECS for API, Vercel for frontend)
Why These Choices
NestJS gave us enterprise-grade structure — dependency injection, guards, interceptors, and a modular architecture that scales with team size.
PostgreSQL RLS meant we could enforce tenant isolation at the database level, not just the application level. Even a bug in our API code can't leak data across tenants.
BullMQ handled our async workloads — rent reminders, payment processing, report generation — with built-in retry logic and dead letter queues.
Implementation Highlights
Financial Safety Layer
For a platform handling money, we implemented multiple safety layers:
- Redis distributed locks on all balance-affecting operations (preventing double-processing)
- Serializable transactions for financial operations (PostgreSQL isolation level)
- Append-only ledger — financial records are never modified, only appended
- Idempotency keys on payment operations (preventing duplicate charges)
Real-Time Dashboard
Property managers needed live data. We implemented:
- Server-Sent Events (SSE) for real-time updates
- Materialized views for dashboard aggregations (refreshed every 5 minutes)
- Optimistic UI updates for instant feedback
Mobile Tenant Portal
Built with React Native (Expo) sharing business logic with the web app:
- Push notifications for rent reminders and maintenance updates
- QR code check-in/check-out for property access
- In-app payment with saved payment methods
- Maintenance request with photo uploads
Results
After 6 months of development and 3 months of gradual rollout:
- 10,000+ active users across 50+ property management companies
- 99.97% uptime over the first year
- 85% reduction in manual payment reconciliation time
- 60% faster maintenance request resolution
- $0 data breaches — row-level security prevented any cross-tenant leaks
Lessons Learned
- Invest in the financial layer early — retrofitting transaction safety is much harder than building it in from the start.
- Multi-tenant testing is critical — every test should verify tenant isolation. We automated this with middleware that checks every database query includes a tenant filter.
- Start with a solid notification system — users expect real-time updates, and adding notifications retroactively touches every feature.
Conclusion
Building a multi-tenant SaaS platform is an exercise in making the right architecture decisions early. Row-level security, transactional integrity, and a clear separation of concerns were the foundations that let us ship features quickly while maintaining enterprise-grade reliability.



