Flagship · Solo build · Oct 2025 – present
FieldDay
A 13-domain monorepo where the architecture rules are executable, not aspirational.
A pre-launch participation platform for People, Organizations, and Sponsors, built solo as one Nx monorepo. Bun + Express + tRPC + Zod over PostgreSQL/PostGIS, where domain isolation, a financial ledger, youth safety, and even product vocabulary are enforced by custom ESLint rules and CI ratchets that fail the build, not by code review.
- 13 / 14
- Backend domains / frontend packages, one Nx monorepo
- 604
- PostgreSQL tables · 429 migrations
- 136
- Architecture checks that fail the build with a reason
- 6
- Custom ESLint rules enforcing DDD boundaries
- 1536-dim
- halfvec HNSW embeddings for hybrid search
- 3,700+
- Commits, solo, since Oct 2025
What it is
One participation graph, three co-primary realms, one app.
Most active-life software owns one slice: Strava the solo athlete, TeamSnap the youth-sports family, Mindbody the studio. FieldDay models the whole graph: the people, the organizations that run activities, and the sponsors that fund them, in a single product.
People
/people/*
Free, with no paywalled coordination, ever
Organizations
/org/*
Paid SaaS + payments take-rate (the revenue engine)
Sponsors
/sponsor/*
Operator-ready; revenue is 2028+ optionality
Pre-launch and local-dev: the design is complete (80 canonical vision docs) and execution runs toward a Phase 1 ship target of December 31, 2026. Everything below is labelled Built / Flag-gated / Designed so the line between shipped code and roadmap is never blurred.
The idea worth stealing
Architecture enforced as code: 6 ESLint rules + 136 architecture checks.
The rules that keep a large system coherent are usually a wiki page nobody reads. Here they are executable. Six custom ESLint rules and 136 architecture checks (96 on every push) fail the build the moment an invariant is violated. Boundaries aren't a convention; they're a compiler error.
// Derives the source domain from the file path and regex-matches the import —
// deliberately NOT the Nx project graph, which silently skips when the graph plugin OOMs:
"Boundary enforcement that disappears when CI is unhappy is not enforcement."- Six custom ESLint rules.Built
no-cross-domain-import,no-database-in-router(the Router→Service→Repository spine),no-raw-event-bus-on(forces a never-throw listener wrapper), plus design-token guards (no-raw-hex,no-arbitrary-tailwind,no-raw-motion-import). - Ninety-six fast architecture ratchets run inBuilt
pre-push.sh(FK-index coverage, transaction discipline, tenant-scoped queries, soft-delete filters, test coverage), each baseline-tracked, so technical debt can only go down, never silently up. - Doctrine as code.Built
vocabulary-checkandpricing-checkban deprecated product terms (Class→Program, Booking→Registration, Payment→Transaction). Product vocabulary is a compile-time invariant.
Skills shown
- Custom ESLint / AST rules
- Architecture-as-code
- CI gate design
- Fitness functions
- DDD boundaries
System architecture
Domain-driven, event-bus-only, type-safe end to end.
Thirteen bounded contexts in one workspace. They never import each other (cross-domain communication flows over an event bus), and every service follows the same spine: Router → Service → Repository. tRPC + Zod give a single source of truth from PostgreSQL to React, with explicit return types throughout and no any.
One Next.js app serves three realms; a Bun + tRPC API fronts a shared core and 13 domains that talk only over an event bus.
Enforced · Domains can't import each other: 6 custom ESLint rules and 136 architecture checks fail the build, not the code review.
The 13 backend domains
peopleAuth, profiles, households, trust scoring, circles
activitiesSessions, programs, registrations, discovery, search
commercePayments, ledger, subscriptions, payouts, dunning
facilitiesVenues, spaces, bookings, staff
engagementNotifications, messaging, reviews, squads, feed
safetyWaivers, incidents, background checks, COPPA consent
authzRole graph, RBAC, capabilities
sponsorsCampaigns, targeting, impact, storefront (40+ routers)
infrastructureAnalytics, jobs, realtime, outbox, tracing
integrationsBooking providers, public APIs, webhooks
tenantsMulti-tenant config, custom domains
calendar-syncCalendar integration (service-only)
widgetsEmbeddable widget analytics
Skills shown
- Domain-Driven Design
- Event-driven architecture
- Nx monorepo
- tRPC + Zod e2e types
- Layered design
The money system
An append-only ledger, a take-rate engine, and the judgment to reject TigerBeetle.
- Append-only transaction ledger.Built
commerce.transactionsis never mutated. It uses quarterly RANGE partitioning oncreated_at, a composite primary key, and aledger_sourceenum (postgres | tigerbeetle) for dual-write tracking. - Differential take-rate. Fees key on aBuilt
discovery_sourceenum (org-direct vs. fieldday-discovered vs. sponsor-funded) × tier → markup in basis points, with a DB-level invariant:amount = facilityCut + platformFee + net. - Dunning state machine. Failed payments retry on an escalation cascade (email → SMS → WhatsApp → in-app), idempotent onBuilt
provider_message_id, with TCPA quiet-hours skips and a hardretry_count < 3ceiling. - TigerBeetle, deliberately deferred. A 795-line evaluation rejects a dedicated financial DB for general transactions (Postgres is correct at ~3,000 tx/month; TigerBeetle only matters near 5,000 tx/sec) and pilots it only as a shadow ledger for sponsor flows, gated behind seven clean days of staging reconciliation.Designed
// UUIDv7's non-zero top byte made the original XOR namespace tag collide ~1/256.
// Fix: clear the top 8 bits, then OR a fixed tag — misrouting becomes impossible.
const BOTTOM_120_MASK = (1n << 120n) - 1n;
account_id = (uuidToU128(sponsor.id) & BOTTOM_120_MASK) | POOL_TAG; // 0x01 pool, 0x02 payablePCI DSS 4.0.1 tooling Built: a weekly leader-elected job (Postgres advisory lock) snapshots payment-page CSP headers and script integrity, routing drift by severity (forensic vs. warning vs. page-the-on-call) into an immutable audit.security_events trail.
Skills shown
- Financial ledger design
- Postgres partitioning
- Stripe Connect
- Billing / take-rate engine
- Distributed ID design (u128)
- Build-vs-buy judgment
- PCI DSS compliance
Search & ML infrastructure
pgvector + Typesense, fused by Reciprocal Rank Fusion.
- Production vector store.Built
halfvec(1536)embeddings with HNSW indexes (m=16, ef_construction=100,halfvec_cosine_ops) plus three entity-scoped partial indexes for filtered vector search at million scale. - Hybrid retrieval. Typesense keyword results and pgvector cosine results are merged by Reciprocal Rank Fusion (Built
score(d) = Σ 1/(k + rank),k = 60), shipped insemantic-search.service.ts. - ML observability in the schema. A model registry, append-only prediction logs, drift checks (PSI / KS / JS divergence), a feature store with TTLs, and per-user GDPR/CCPA ML-consent tracking.Built
- AI operator services, behind flags. Five AI-native features (scheduling, demand forecasting, churn, copy, attribution) are designed with non-AI fallbacks; the plan is explicit that there are zero Claude API calls in any router today. Bedrock model routing is a decision, not yet code.Designed
Skills shown
- pgvector / HNSW ANN
- Hybrid retrieval (BM25 + dense + RRF)
- ML observability
- Drift detection
- Feature store
Trust, safety & access
Trust written once, youth safety enforced in the backend.
- Trust written once, read everywhere.Built
TrustTieris computed by exactly one service from five weighted factors (attendance 0.4, cancellation-notice 0.2, payment 0.2, account-age 0.1, verification 0.1). A Postgres trigger mirrors a CHECK constraint; every other realm reads the tier, never the raw score. - Youth safety is structural. Adult↔minor 1:1 DMs are blocked in the backend (SafeSport MAAPP), COPPA consent is required under 13, and guardian signatures are binding on minor waivers. These are guarantees the UI can't undo.Built
- Capability-based RBAC. Effective capabilities are computed across realms and enforced at the tRPC boundary byBuilt
requireCapabilitymiddleware, with an event-sourced audit outbox (aggregate version + sequence number, monotonic viaGREATEST()). - OpenFGA / Zanzibar direction. A relation-based authorization model is being laid in (anDesigned
entityRelationshipstable, 20 capability↔relation mappings). It's blueprinted, not yet the live path.
Skills shown
- Reputation systems
- Single-source-of-truth
- RBAC / capabilities
- Relation-based authz
- COPPA / SafeSport
- Event sourcing + outbox
Worldwide-first data
Internationalization is a database constraint, not a backlog item.
- Constraints, not conventions. Profiles enforce ISO 3166-1 country codes and E.164 phones via a CHECK regex; money isBuilt
{ amount: bigint, currency: ISO 4217 }(never a float), and 24 organization types are enumerated for multi-vertical support from day one. - 604 tables, 429 migrations. Drizzle is the source of truth (latestBuilt
0631); no rollbacks. High-volume tables are time-partitioned, IDs are UUIDv7, and every migration-hygiene rule is its own ratchet with a baseline. - Per-jurisdiction privacy regimes. GDPR / CCPA / LGPD / PIPA routing is doctrine-described and partly schema-routed by country code; external audits are deferred until 100K users by the data doctrine itself.Designed
Skills shown
- i18n data modeling
- Money as bigint
- DB constraint design
- Forward-only migrations
- Partitioning / indexing
- Privacy-by-design
Product doctrine
A refusal to ship the dark patterns the category relies on.
These are binding. They shape the schema and are enforced by the vocabulary and pricing ratchets, not left to product whim.
- No loss-on-miss streaks: show “3 of the last 4 weeks,” never a punishing strike-out
- No public vanity metrics: likes, follower and view counts are never shown to others
- No infinite algorithmic feed: every feed is finite and ends with “you’re caught up”
- No paywalled coordination: RSVP, calendar, messaging and the annual recap are always free
- No rebrandable trust or safety: trust and auth surfaces are locked from tenant theming
The stack
Every version taken from a committed package.json.
Runtime & language
- TypeScript 6 (strict)
- Bun 1.3
- Nx 22
API
- Express 4 + tRPC 11
- Zod
- @fieldday/contracts
Data
- PostgreSQL 18 + PostGIS
- Drizzle 0.44
- pgvector (halfvec/HNSW)
- DragonflyDB
- Typesense 27
- MinIO / S3
Web & mobile
- Next.js 16
- React 19
- TanStack Query
- Tailwind · shadcn/ui
- React Native
Payments & comms
- Stripe Connect 19
- Twilio
- Socket.io
- BullMQ
Quality & infra
- Vitest · Playwright
- OpenTelemetry + Pino
- Terraform
- Docker
Honest status
What's shipped, what's flag-gated, what's still a doc.
Built
- The monorepo + 13 domains + the ESLint / ratchet enforcement layer
- Append-only ledger, differential take-rate, dunning, PCI tamper tooling
- pgvector + Typesense hybrid search, ML observability schema
- Trust scoring, capability RBAC, backend-enforced youth safety, worldwide schema
Flag-gated
- Sponsor v2 surface: 70+ routes behind sponsor.v2.* flags
- Lifespan-persona acquisition behind gtm.* floors
- Sponsor v1 is operator-ready; revenue is explicitly 2028+ optionality
Designed
- Five AI operator services (zero Claude calls in any router today)
- AWS Bedrock model routing; TigerBeetle production dual-write
- Full OpenFGA cutover, multi-region infra, external privacy audits
This split is the point, not a disclaimer. The numbers above are taken from the live repository (429 migrations, latest 0631; 6 ESLint rules; halfvec(1536) at m=16), and a design doc is never dressed up as shipped code.
The systems half of a forward-deployed engineer.