Property PrismDev Hub

System Architecture Overview

High-level architecture, tech stack, service boundaries, and data flow for Project Prism.

Updated Apr 3, 2026

What Is Prism

Prism is a commercial real estate (CRE) intelligence platform for industrial property markets. It provides a centralized system for tracking buildings, lease comparables (comps), tenant-in-market activity (TIMs), ownership, brokerage relationships, analytics, and reporting — all within a multi-tenant, organization-isolated environment.


High-Level Architecture

┌──────────────────────────────────────────────────────────────────────────┐
│                            End Users                                     │
│                   (Browsers / BI Tools / API Clients)                    │
└─────────────────────────────┬────────────────────────────────────────────┘
                              │
                    ┌─────────▼──────────┐
                    │   Cloudflare Edge   │  ← TLS termination, WAF, caching
                    │   (CDN + Proxy)     │
                    └──┬──────────────┬───┘
                       │              │
          ┌────────────▼──┐    ┌──────▼──────────┐
          │  Cloudflare   │    │  Render          │
          │  Pages        │    │  (Go Backend)    │
          │  (Frontend)   │    │  Port 8080       │
          └───────────────┘    └──────┬───────────┘
                                      │
                         ┌────────────┼────────────┐
                         │            │            │
                  ┌──────▼──┐  ┌─────▼────┐ ┌─────▼─────┐
                  │ Postgres │  │  Redis   │ │  Clerk    │
                  │ + PostGIS│  │ (Cache)  │ │  (Auth)   │
                  │ (Supabase│  │ (Upstash)│ │  (JWT)    │
                  │  / RDS)  │  │          │ │           │
                  └──────────┘  └──────────┘ └───────────┘

Service Boundaries

ServiceTechnologyHostingPurpose
FrontendReact 19 + Vite + TypeScriptCloudflare PagesSPA for all user interactions
Backend APIGo (Chi router)RenderREST API, business logic, auth enforcement
DatabasePostgreSQL 15 + PostGISSupabase (managed)Persistent storage, spatial queries, RLS
CacheRedisUpstash (managed)Query caching, rate limit state
Auth ProviderClerkClerk (SaaS)SSO, MFA, JWT issuance, org management
SecretsInfisicalInfisical (SaaS)Centralized secrets, auto-sync to platforms
Error TrackingSentrySentry (SaaS)Frontend + backend error/performance monitoring
Marketing SiteNext.jsCloudflare PagesPublic marketing website
Docs SiteNext.js + Fumadocs(Dev/staging)User-facing documentation

Tech Stack

Frontend

CategoryTechnology
FrameworkReact 19 + TypeScript
Build ToolVite
RoutingTanStack Router (code-based route tree)
Server StateTanStack Query
TablesTanStack Table
UI LibraryMantine 7
FormsReact Hook Form + Zod validation
MapsMapLibre GL JS
ChartsECharts (via echarts-for-react)
AuthClerk React SDK
TestingVitest + React Testing Library + Playwright
LintingESLint + Prettier

Backend

CategoryTechnology
LanguageGo 1.22+
HTTP Routergo-chi/chi v5
Database Driverjackc/pgx v5 (connection pooling)
Cache Clientgo-redis/redis v9
AuthClerk JWT validation (JWKS)
Logginggo.uber.org/zap (structured JSON)
MetricsPrometheus client (promhttp)
API Docsswaggo/swag (Swagger/OpenAPI generation)
CSV Processingencoding/csv (stdlib)
TestingGo stdlib testing + testcontainers-go

Database

CategoryTechnology
EnginePostgreSQL 15
ExtensionsPostGIS (spatial), pg_cron (optional scheduling)
SecurityRow-Level Security (RLS) policies
ExportDedicated export schema for BI tools

Infrastructure

CategoryTechnology
CI/CDGitHub Actions
ContainerizationDocker + Docker Compose
Monitoring (local)Prometheus + Alertmanager + Grafana
Monitoring (prod)Sentry + Render built-in metrics
Load Testingk6
ObservabilityGrafana Alloy (OpenTelemetry collector, local)

Backend Architecture Pattern

The Go backend follows Clean Architecture (Ports & Adapters) with strict layering:

┌─────────────────────────────────────────────────────────────┐
│                    HTTP Layer (Handler)                      │
│  - Parse HTTP requests, validate input format               │
│  - Call service layer                                       │
│  - Format HTTP responses                                    │
│  - NO business logic                                        │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                   Service Layer (Business Logic)            │
│  - All business rules and validation                       │
│  - Orchestrate multiple repositories                       │
│  - Cache management                                        │
│  - NO HTTP knowledge, NO SQL queries                       │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│              Repository Layer (Data Access)                  │
│  - SQL queries and data mapping                            │
│  - Interface-based (swappable)                             │
│  - NO business logic                                       │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                    Database (PostgreSQL)                     │
└─────────────────────────────────────────────────────────────┘

Dependency Rule: Source code dependencies point inward only. Inner layers never depend on outer layers. Services depend on repository interfaces, not implementations.

Cross-Cutting Concerns

ConcernImplementation
AuthenticationClerk JWT middleware on all protected routes
AuthorizationScope-based middleware (RequireScope)
Org IsolationOrg ID extracted from JWT, passed to repository queries
Rate LimitingToken bucket per IP (configurable burst/rate)
Request TracingInjectRequestID middleware, propagated to error responses
LoggingStructured zap logger injected into all layers
CachingRedis with pattern-based invalidation on mutations
Error HandlingCentralized apierror package with uniform envelope
MetricsPrometheus counters/histograms on all routes
RecoveryPanic recovery middleware

Frontend Architecture Pattern

The frontend follows a module-based architecture organized by feature domain:

frontend/src/
├── app/                    # App shell, providers, global styles
├── modules/                # Feature modules (one per domain)
│   ├── buildings/          # Building search, detail, edit
│   ├── comps/              # Comp lease search, detail, edit
│   ├── tims/               # TIM search, detail, edit
│   ├── dashboard/          # Main dashboard
│   ├── map/                # Explore map (multi-layer)
│   ├── analytics/          # Vacancy, absorption, pipeline, inventory
│   ├── operations/         # Needs review, missing data, recent changes
│   ├── data/               # Owners, brokers, leasing cos, reports, parks
│   ├── admin/              # Imports, errors, system setup, data export
│   └── lookups/            # Shared lookup data (regions, corridors, etc.)
├── shared/                 # Cross-module shared code
│   ├── api/                # Typed API client, query keys, error handling
│   ├── ui/                 # Shared UI components
│   ├── hooks/              # Shared hooks
│   ├── map/                # Shared map utilities
│   └── utils/              # Formatting, validation utilities
└── test/                   # Test setup and utilities

Key Frontend Patterns

  • Server state: TanStack Query for all API data (no global stores)
  • Form state: React Hook Form + Zod schemas
  • URL state: Filter/sort/pagination persisted in URL search params
  • Module isolation: No cross-module imports except through shared/
  • API layer: All API calls through typed wrappers (never raw fetch)

Data Model Overview

The database has two scoping models:

Public Data (No Org Isolation)

Platform-wide reference data accessible to all authenticated users:

  • Buildings — physical properties with address, classification, metrics
  • Geography — regions, corridors, submarkets (with PostGIS boundaries)
  • Lookups — size tranches, amp tranches, building parks, rail lines, NAICS codes, owner types
  • Key Points — named locations (ports, airports, interchanges) for distance calculations
  • Labor Data — census block group labor statistics with polygon boundaries

Org-Scoped Data (Org Isolation Enforced)

Private data belonging to a specific organization:

  • Comp Leases — lease transaction records with terms, rent, tenant/owner links
  • TIMs — tenant-in-market activity tracking with status workflow
  • Owners — property owner entities with type classification
  • Tenants — tenant entities with NAICS code classification
  • Contacts — people linked to owners, tenants, or brokerage firms
  • Brokerage Firms — brokerage company entities
  • Broker Assignments — relationships between brokers and owners/firms
  • Reports — named collections of buildings, comps, and TIMs
  • Building Overrides — org-private annotations on shared buildings
  • Audit Log — append-only record of all data changes
  • Import Jobs — CSV import job tracking
  • Error Log — API error telemetry
  • API Keys — service-to-service authentication tokens

Export Schema

A separate export schema provides denormalized, BI-tool-friendly tables:

  • export.export_buildings — platform-wide denormalized buildings
  • export.buildings_export — org-scoped merged building + metrics + overrides
  • export.export_comp_leases — org-scoped denormalized comps
  • export.export_tims — org-scoped denormalized TIMs

Refresh functions rebuild export tables on demand or via scheduled jobs.


Authentication & Authorization Flow

┌──────────┐     ┌──────────┐     ┌──────────────┐     ┌──────────┐
│  User    │────▶│  Clerk   │────▶│  Frontend    │────▶│  Backend │
│ (Browser)│     │  (SSO)   │     │  (Bearer JWT)│     │  (Verify)│
└──────────┘     └──────────┘     └──────────────┘     └──────────┘
                                                              │
                                                    ┌─────────▼─────────┐
                                                    │ Extract from JWT: │
                                                    │ - user_id         │
                                                    │ - org_id          │
                                                    │ - org_permissions │
                                                    │   (scopes)        │
                                                    └───────────────────┘
  1. User authenticates via Clerk (SSO/MFA)
  2. Frontend obtains JWT from Clerk React SDK
  3. Frontend sends Authorization: Bearer <token> on all API requests
  4. Backend validates JWT signature via Clerk JWKS endpoint
  5. Backend extracts user ID, org ID, and permission scopes from JWT claims
  6. Backend middleware enforces required scopes per route
  7. Backend repository layer filters all org-scoped data by org ID

Role Model

RoleScopesUse Case
viewerAll *:read scopesRead-only stakeholders
memberAll *:read + *:write + exportStandard operational users
adminAll member scopes + api_keys:manage, audit:read, export:adminOrg owners/operators

API Contract

  • Base path: /api/v1
  • Auth: Clerk JWT Bearer token (all protected routes)
  • Content type: JSON request/response
  • Error envelope: { code, message, details?, request_id }
  • List response: { data: [], total, has_more }
  • Pagination: Backend-driven via query params (page, page_size, sort, order)
  • OpenAPI: Auto-generated via swaggo, drift-checked in CI

Error Codes

CodeMeaning
not_foundResource does not exist
validation_errorInput validation failed (field details in details)
unauthorizedMissing or invalid auth token
forbiddenAuthenticated but insufficient permissions
conflictDuplicate or constraint violation
internal_errorServer error

Release & Deployment Flow

feature branch → qa → [CI + QA validation] → PR: qa → main → production
  1. Feature branches merge into qa via PR
  2. CI runs: Go tests, race detection, vet, Swagger drift check, Docker compose validation
  3. QA validation on staging
  4. PR from qa to main requires review + green CI
  5. Merge to main triggers production deploy
  6. Post-deploy: verify /health, /ready, spot-check key endpoints

Hosting

ComponentPlatform
FrontendCloudflare Pages (static SPA)
BackendRender (Docker container)
DatabaseSupabase (managed Postgres)
CacheUpstash (managed Redis)
SecretsInfisical → auto-synced to Render + Cloudflare