Infinity Gym
Gym management: a .NET API, an admin CMS, a mobile-first member app, and a bilingual landing page.
- Year
- 2026 – now
- Role
- Co-founder and developer, drunkcode.dev
- Type
- Platform · API, admin, member app
- Stack
- .NET 10 · PostgreSQL · Redis · Next.js 16 · React 19 · TanStack Query · Tailwind v4




Problem
A gym runs memberships, freezes, classes, personal training, lockers, payments, and check-in. Staff need one admin surface for all of it, and members need one app on their phone instead of a front desk and a chat thread.
Constraints
- One domain model for members, subscriptions, payments, attendance, and training, even where the API does not expose it yet.
- Sessions must be revocable: signing out or changing a password ends access everywhere, not when a token expires.
- The admin menu is driven by permissions, so staff only see what their role allows.
- The member app had to cover every flow in the design before the API existed for it, and it was built by an AI coding agent, so every change needed review against the design and the API contract.
System
- API
- .NET 10 modular monolith with Admin and Members modules over one shared domain and one PostgreSQL DbContext. Thin controllers, concrete services, typed exceptions mapped by a global handler.
- Auth
- JWT access and refresh tokens in HttpOnly cookies, with the session truth in Redis: active sessions, hashed refresh tokens, and a blacklist of revoked token IDs. Permissions are string policies checked per endpoint.
- Admin CMS
- Next.js 16 and React 19 with its own token-based design system. Each backend module maps to a vertical slice: page, organism, hook, TanStack Query wrapper, and a server-side API service.
- Member app and landing
- A mobile-first Next.js member app covering onboarding, check-in QR, classes, personal training, lockers, and membership payment, built on mocked services with the API's shapes. The landing page is Next.js with next-intl, Indonesian first.
Decisions
1.
Modular monolith, one database
Two modules, one host, one DbContext. Modules never reference each other; they meet only in the shared domain.
Tradeoff Boundaries are kept by project references and review, not by the network.
2.
Redis owns the session
Tokens prove identity; Redis decides whether the session still exists. Revocation is a key delete, not a wait for expiry.
Tradeoff Every authenticated request depends on Redis being up.
3.
One slice per backend module
The CMS mirrors the API: one folder per module with the same three service files. A new endpoint has an obvious home.
Tradeoff More files per feature, and the shape has to be kept even for small screens.
4.
Mock first, same shapes
The member app's services return the API's response shapes from a shared in-browser store, so screens and states can be finished before the endpoints exist.
Tradeoff Nothing persists yet; the integration work is still ahead.
5.
Agent-built, human-reviewed
I directed omp, an AI coding agent, to build the member app from the design frames: I wrote the briefs module by module, reviewed every change, and held it to the API's response shapes and the design's states.
Tradeoff Review becomes the bottleneck, and the brief has to be precise; a vague brief produces confident, wrong screens.
Screens

Status
Built at drunkcode.dev; in development. Auth, menus, and members are live in the API; subscriptions, payments, attendance, and training exist as entities and seed data; the member app runs on mocked services. Private repositories.
Evidence
- Private repositories