Skip to content

System overview

Real estate platform for Mexico (bilingual es-MX / en-US) with PostGIS geographic capabilities. Turborepo monorepo running on Bun.

graph TB
    subgraph Client
        Browser["Browser (SvelteKit SSR + CSR)"]
        Mobile["Mobile (native iOS/Android)"]
    end

    subgraph Dokploy["Dokploy (Hostinger VPS)"]
        SvelteKit["SvelteKit web app"]
        API["apps/api (Hono on Bun)"]
        Worker["BullMQ worker"]
        Postgres["PostgreSQL + PostGIS 16"]
        Valkey["Valkey (Redis-compatible)"]
        Meilisearch["Meilisearch v1.9"]
        imgproxy["imgproxy v3.30"]
    end

    Bucket["Cloudflare R2 (S3-compatible)"]

    subgraph External["External"]
        Resend["Resend (Email / OTP)"]
        GoogleMaps["Google Maps Platform"]
        Sentry["Sentry"]
        Google["Google OAuth"]
        Stream["GetStream (chat)"]
        Knock["Knock (notifications)"]
        Replicate["Replicate (AI staging)"]
        Gemini["Gemini 2.5 Flash"]
    end

    Browser --> SvelteKit
    Browser --> API
    Mobile --> API
    SvelteKit --> Postgres
    SvelteKit --> Valkey
    SvelteKit --> Meilisearch
    SvelteKit --> Bucket
    SvelteKit --> Resend
    SvelteKit --> GoogleMaps
    SvelteKit --> Sentry
    SvelteKit --> Google
    API --> Postgres
    API --> Valkey
    Worker --> Postgres
    Worker --> Valkey
    Worker --> Replicate
    Worker --> Gemini
    Worker --> Knock
    SvelteKit --> Stream
    Bucket --> imgproxy
    imgproxy --> Browser

Inter-service communication stays on Dokploy’s internal Swarm network (dokploy-network); the data services publish no ports. Only the SvelteKit app, apps/api, imgproxy, and the docs site have public domains (Cloudflare → Traefik). Object storage is Cloudflare R2, reached over the S3 API.

LayerTechnologyVersion
RuntimeBun1.3+
Web frameworkSvelteKit + Svelte 52.50 / 5.49
Mobile frameworkSwift + SwiftUI (iOS 17+) / Kotlin + Jetpack Compose (SDK 26+)
DatabasePostgreSQL + PostGIS16
Query builderKysely (on pg) — typed, no ORM
AuthBetter Auth — email OTP + Google OAuth1.4
State (web)Svelte 5 runes ($state, $derived, $effect)
State (mobile)@Observable (iOS) / StateFlow ViewModels (Android)
Search (locations)Meilisearch0.55
Search (properties)@repo/redis-search — Valkey GEOSEARCH
File StorageCloudflare R2 (S3-compatible)
Image Processingimgproxy (HMAC-signed URLs)3.30
Styling (web)Tailwind CSS4.1
Styling (mobile)Generated design tokens (SwiftUI / Compose)
UI Components (web)bits-ui (headless) + formsnap2.14
UI Components (mobile)Hand-built per-app primitive libraries (lockstep iOS/Android)
Forms (web)sveltekit-superforms + Zod2.29 / 4.3
i18nParaglide JS2.10
IconsLucide Svelte (web) / SF Symbols + Material icons (native mobile)
ChatGetStream.io
NotificationsKnock
EmailResend
MapsGoogle Maps Platform
Background jobsBullMQ (on Valkey)
Error TrackingSentry
TestingPlaywright (E2E), Bun Test (unit)1.58
CI/CDGitHub Actions
DeployDocker + Dokploy (dev, Hostinger VPS)
real-estate-core/
├── apps/
│ ├── svelte-web/ # SvelteKit web app (public + dashboards)
│ ├── ios-user/ # Native iOS customer app (Swift + SwiftUI)
│ ├── ios-agent/ # Native iOS agent app (Swift + SwiftUI)
│ ├── android/ # Gradle umbrella build for both Android apps
│ │ ├── app-user/ # Native Android customer app (Kotlin + Compose)
│ │ ├── app-agent/ # Native Android agent app (Kotlin + Compose)
│ │ └── core/design-system/ # :core:design-system — generated Tokens.kt + Compose primitives
│ ├── worker/ # BullMQ background worker
│ ├── api/ # Standalone Hono server (tRPC + Better Auth)
│ └── docs/ # This documentation site
├── packages/
│ ├── api/ # Shared tRPC routers + services
│ ├── contract/ # Zod wire schemas → Swift/Kotlin model codegen
│ ├── database/ # Kysely client (pg) + SQL migrations + generated types
│ ├── redis-search/ # Property geo-search (Valkey GEOSEARCH)
│ ├── cache/ # Valkey/Redis utilities
│ ├── validation/ # Shared Zod schemas
│ ├── auth/ # Better Auth wiring (web + API)
│ ├── notifications/ # Knock translations + email layout
│ ├── server-services/ # Shared server helpers (IP-trust, rate limit, email)
│ ├── eslint-config/ # Shared ESLint rules
│ └── typescript-config/ # Shared TS configs
├── docs/ # Source-of-truth Markdown (mirrored by this site)
├── context/ # Local-only design notes (gitignored)
├── turbo.json # Turborepo pipeline
└── package.json # Root workspaces ([email protected])
  1. SvelteKit over Next.js — smaller bundles, simpler reactivity (Svelte 5 runes), unified server/client.
  2. Valkey GEOSEARCH over Typesense — native Redis geo-filtering; Meilisearch handles location text search separately.
  3. imgproxy over build-time processing — on-demand resizing with HMAC-signed URLs; no pre-generation.
  4. Better Auth over Supabase Auth — self-hosted with full OTP control (via Resend) + Google OAuth.
  5. bits-ui over opinionated UI frameworks — headless, accessible primitives styled with Tailwind.
  6. S3-compatible object storage over Supabase Storage — endpoint-swappable (@aws-sdk/client-s3); currently Cloudflare R2 (previously Railway Bucket), zero code changes to move.
  7. Paraglide JS over runtime i18n — compile-time translations for zero-runtime overhead and tree-shaking.
  8. Portal + Dashboard split — personal user activities (/portal) separated from agent workflows (/dashboard).
  9. Form actions on web, tRPC on mobile — web mutations are SvelteKit form actions (progressive enhancement); mobile speaks tRPC. See Conventions → Web and Conventions → tRPC.
  10. Standalone apps/api — mobile and (eventually) browser tRPC traffic go through a Hono server on port 4000 that mounts both /api/auth/* and /api/trpc/*. SvelteKit hosts the same Better Auth handler for its own web flow.