Matchpoint — where athletes and brands grow together
CompletedA sponsoring platform for athletes and brands — 46 features in 29 days, built on an architecture that lasts.
What was built
A system of record for athlete-brand sponsoring. Athletes create profiles, brands set up organizations with teams, partnerships move through a defined lifecycle — from first contact to active sponsorship. Public athlete directories, shareable media kits, a notification system, and a vendor admin interface for reference data.
Four deployable applications: API, worker, web UI, and landing page. One monorepo, one shared type system, independent deployments. The domain had no software before — everything ran on emails, DMs, and spreadsheets.
The constraint
This system becomes the foundation for everything that follows: metrics, content aggregation, federation integration, service provider connections. Whatever architecture stands now has to hold. Not for three months — for years.
The decision was not to show something fast. It was to build something you can stand behind. Architectural discipline was not a luxury — it was the condition for speed not becoming a liability later.
The architecture
Core-first, ports and adapters. All domain logic lives in its own package — no NestJS, no Prisma, no Supabase. Framework-agnostic, testable, portable. The API layer adapts. The core layer decides.
Authorization via ABAC with CASL, not RBAC. Policies evaluate context: user, organization, resource, role. A single user can be vendor, brand admin, and athlete simultaneously — the rules stay consistent. CASL policies are defined in core, enforced in the API layer, and backed by RLS in the database as an additional safety net.
For reliable side effects, an outbox pattern: a partnership state change creates an outbox entry in the same transaction. A worker service polls and processes asynchronously — notifications, events, external actions. Not just on a whiteboard. Validated in production.
What made the speed possible
46 features in 29 days sounds like a prototype. It was not.
Spec-driven development: every feature has a spec, gets challenged, then gets built. Domain docs emerge during implementation, not after. The spec defines what gets built. The domain doc records what stands. No rework from misunderstandings, because the questions get answered first.
The monorepo eliminates coordination. Shared types between core, API, and web — when a contract changes, the build breaks immediately. No versioning, no publish cycles, no integration surprises.
The discipline of keeping the core layer clean saved more time than any shortcut could have.
What remains
Building a system of record means building a foundation. The architecture has to be right on day one — not because it needs to be perfect, but because it must not block tomorrow’s decisions. Portability was not an exercise but a deliberate choice: Supabase as infrastructure, not as a cage. Prisma migrations as pure SQL. No vendor lock-in that becomes expensive later.
Speed did not come despite the discipline. It came because of it.