Architecture
Multi-service design, data flow, publish pipeline, and service boundaries for the ActivityFox platform.
ActivityFox — Technical Documentation
Developer-focused documentation covering architecture, data models, APIs, and integration guides.
System Architecture
%%{init: {'theme': 'neutral'}}%%
graph TB
Browser["React SPA<br/>(shadcn/ui + Tailwind + Redux)"]
Express["Express.js API Server"]
ST["Sharetribe<br/>Users · Listings · Transactions"]
SB["Supabase<br/>Activities · Sessions · Preferences"]
Davis["Davis CalDAV<br/>Calendars · Events"]
Meili["Meilisearch<br/>Search Indexes"]
n8n["n8n<br/>Sync Workflows"]
Browser -->|"Redux thunks"| Express
Browser -->|"Marketplace SDK"| ST
Express -->|"Integration SDK"| ST
Express -->|"Service role"| SB
Express -->|"tsdav + ical.js"| Davis
Express -->|"meilisearch client"| Meili
n8n -->|"Supabase → Meili sync"| Meili
n8n -->|"Seat tracking"| SB
n8n -->|"Webhooks"| STActivityFox is a multi-service platform. Each service owns a specific domain; the Express.js API server orchestrates between them.
Service Boundaries
%%{init: {'theme': 'neutral'}}%%
graph LR
subgraph Frontend
SPA["React SPA<br/>shadcn/ui · Tailwind · Redux"]
end
subgraph "Express.js API"
API["API Router<br/>server/apiRouter.js"]
end
subgraph "External Services"
ST["Sharetribe<br/>Users · Listings<br/>Transactions · Payments"]
SB["Supabase Cloud<br/>Activities · Sessions<br/>Preferences · Vendor Signups"]
Davis["Davis CalDAV<br/>Calendars · Events<br/>Recurrence (rrule)"]
Meili["Meilisearch<br/>sessions index<br/>activities index"]
n8n["n8n Workflows<br/>Sync · Seat Tracking<br/>Like Counts"]
end
SPA --> API
SPA -->|"Marketplace SDK"| ST
API -->|"Integration SDK"| ST
API -->|"@supabase/supabase-js"| SB
API -->|"tsdav + ical.js"| Davis
API -->|"meilisearch client"| Meili
n8n --> SB
n8n --> Meili
n8n --> STWhat Lives Where
| Service | Data Owned | Why |
|---|---|---|
| Sharetribe | User accounts, marketplace listings, transactions, payments | Core marketplace primitives — auth, payments, trust |
| Supabase | Activities, sessions, child preferences (likes, connections, interests), vendor signups | Relational data that doesn't fit Sharetribe's publicData/privateData model |
| Davis (CalDAV) | Calendars, events, recurrence rules | Standard calendar protocol with native rrule support |
| Meilisearch | Search indexes (sessions, activities) | Fast faceted search with geo, compatible with Algolia InstantSearch |
| n8n | Workflow definitions | Background sync jobs — no persistent data |
Data Flow
Session Publish Pipeline
When a vendor publishes a draft session, four systems are updated in a single API call:
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Vendor as Vendor Browser
participant API as Express API
participant SB as Supabase
participant ST as Sharetribe
participant Davis as Davis CalDAV
participant MS as Meilisearch
Vendor->>API: POST /api/sessions/publish
API->>SB: Read session + parent activity
SB-->>API: Session and activity data
API->>ST: Create listing<br/>(stock, price, publicData)
ST-->>API: listingId
API->>SB: Update session<br/>(listing_id, status=published)
API->>Davis: Create calendar +<br/>WEEKLY recurring events
Davis-->>API: calendarId
API->>SB: Update session<br/>(caldav_calendar_id)
API->>MS: Index to sessions +<br/>activities indexes
API-->>Vendor: 200 OKData Derivation
Supabase is the source of truth for activity and session data. Other systems hold derived copies:
%%{init: {'theme': 'neutral'}}%%
graph TD
SB["Supabase<br/>(source of truth)"]
ST["Sharetribe Listing<br/>(bookable copy)"]
Davis["CalDAV Calendar<br/>(schedule copy)"]
MS["Meilisearch Index<br/>(searchable copy)"]
SB -->|"on publish"| ST
SB -->|"on publish"| Davis
SB -->|"on publish / n8n sync"| MSBackground Sync (n8n)
| Workflow | Trigger | Flow |
|---|---|---|
| Full index sync | Cron schedule | Supabase → read all published sessions → rebuild Meilisearch indexes |
| Seat tracking | Sharetribe transaction webhook | Transaction event → update seats_remaining in Supabase → update Meilisearch |
| Like count | Cron schedule | Aggregate child_likes per activity → update likeCount in Meilisearch activities index |
Server Utilities
| File | Purpose |
|---|---|
server/api-util/supabaseClient.js | Supabase client with requireSupabase() factory |
server/api-util/meilisearchClient.js | Meilisearch client with index initialization |
server/api-util/davisClient.js | tsdav client factory, ICS parsing/building, vendor provisioning |
server/api-util/integrationSdk.js | Sharetribe Integration SDK singleton |
server/api-util/childOwnership.js | Bidirectional parent-child ownership verification |
server/api-util/crypto.js | AES-256-GCM encryption for child credentials |
Key Design Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Session storage | Supabase Cloud | Sharetribe publicData won't scale for structured session queries |
| Listing model | One Sharetribe listing per session | Clean stock/availability tracking, no publicData overflow |
| Search | Meilisearch + Algolia InstantSearch adapter | API-compatible with Algolia client libraries, self-hosted |
| Component strategy | Never modify Sharetribe originals | Create replacements and swap at route level for upstream rebase safety |
| Data flow | Redux ducks only | No fetch() in components — consistency and testability |
| CalDAV vendor accounts | Auto-provision on first session publish | Zero vendor friction |