PropertyOps was conceived as a vertical SaaS solution to a horizontal data problem: the fragmentation of operational data in mid-to-high density residential management. Most platforms prioritize either the tenant experience or the manager's ledger, leaving the actual operational workforce—maintenance staff and contractors—to rely on disparate spreadsheets or verbal communication. I architected PropertyOps to solve this through a unified, role-aware event bus and a highly relational data model.
1. The Relational Backbone: Precision Database Modeling
The core of PropertyOps is a PostgreSQL database managed via **Prisma ORM**. Unlike simpler "unit-tenant" models, I engineered a hierarchical schema that accounts for the complex reality of property ownership and management. The challenge was maintaining performance as we traversed from a `Property` level down to individual `MaintenanceTicket` and `Transaction` logs.
I utilized recursive relations and strict referential integrity to ensure that data remains consistent even when assets are transferred or tenancies are terminated. To optimize for the Management Dashboard, I implemented composite indexes on high-churn tables like `MaintenanceTicket` (indexing `status` and `priority` alongside `propertyId`).
// Optimized Schema for Operational Flow
model MaintenanceTicket {
id String @id @default(cuid())
title String
status Status @default(OPEN)
priority Priority @default(MEDIUM)
propertyId String
property Property @relation(fields: [propertyId], references: [id])
assignedTo String?
worker User? @relation("WorkerTasks", fields: [assignedTo], references: [id])
reporterId String
reporter User @relation("ReportedBy", fields: [reporterId], references: [id])
@@index([propertyId, status])
@@index([assignedTo])
}
2. Engineering the Role-Based Access Control (RBAC)
With three distinct user roles (Managers, Workers, Tenants), security couldn't be an afterthought. I implemented a robust RBAC system that operates at multiple layers of the stack:
- Middleware Layer: Protecting Next.js App Router segments using JWT session headers to prevent unauthorized route access.
- API Layer: Validating that a User's role ID matches the required permissions for specific CRUD operations (e.g., a Worker can PATCH a ticket status but not DELETE a Property).
- UI Layer: Using tailored React components that only render specific dashboard modules based on the active session's permission set.
3. Real-Time Dashboard Orchestration
For property managers, static data is dead data. I built the Analytics Engine using **Recharts**, but the real engineering lies in the data aggregation layer. I wrote optimized Prisma queries that perform count and sum aggregations at the database level rather than the application level, significantly reducing the "Time to Interactive" for large portfolios.
To ensure a premium feel, I used **Framer Motion** to animate the transitions between dashboard tabs, creating a spatial mental model for the user that makes navigating complex data feel fluid and intuitive.
// Aggregation logic for Manager Insights
const metrics = await prisma.maintenanceTicket.groupBy({
by: ['status'],
_count: { _all: true },
where: { property: { managerId: session.user.id } }
});
4. Scalability and Performance
Building for Next.js 15 allowed me to leverage **Server Actions** for form submissions (like rent payment and ticket reporting), reducing client-side JavaScript overhead. I also implemented aggressive caching strategies for static property assets while keeping the maintenance event bus highly reactive.
The result is a platform that handles the complexity of property management with the speed of a modern single-page application, proving that "enterprise-grade" doesn't have to mean "slow and clunky."