A comprehensive, highly-concurrent SaaS platform built for boutique fitness clubs. Engineered from the ground up using .NET 9, React + TypeScript, and PostgreSQL. Designed with Clean Architecture principles, enforcing a strict separation of concerns and robust transactional safety.
- Project Overview & Live Demo
- Key Business Features
- Architectural Marvels (Tech Deep Dive)
- Technology Stack
- Project Structure
- Getting Started (Local Deployment)
- Test Scenarios
- Deployment & Infrastructure
- Error Handling and Diagnostics
GymCore serves as a full-fledged ecosystem bridging the gap between club members, personal trainers, and facility administrators. It seamlessly handles subscription life-cycles, strict facility access, dynamic pricing, and concurrent booking engines.
Production Environment: Fully containerized and deployed on a VPS via Nginx Reverse Proxy.
π Live Demo: gymcore.com.pl
To quickly log in and test it, here's an example login:
- member1@gmail.com | Member123!
- mariusz@gmail.com | Coach123!
For obvious reasons, I won't include the admin login details :)
Memberships are managed by an advanced state machine. Integration with the Stripe API enables secure, asynchronous payments.
- Dynamic States: Real-time status monitoring (Active, Expiring, Grace Period).
- Asynchronous Billing: Full transaction processing via secure webhooks.
- Discount Engine: Backend discount calculation before payment is finalized.
The reservation system handles high traffic volumes and concurrent requests, ensuring smooth operation during peak hours.
- Concurrency Control: Database-level locks eliminate overbooking.
- Real-time Capacity: Instant updates of available spots in group sessions.
- Flexible Logic: Supports group classes and individual one-on-one sessions with trainers.
Advanced tools optimize navigation and management processes within the club.
- Google Maps API: Quick club location and routing for new members.
- DeepSeek AI Support: Intelligent analytics on occupancy and revenue in real time.
- Management Guidance: Proactive recommendations for optimizing the activity schedule.
GymCore eliminates the need for physical membership cards.
- Encrypted QR Codes: Unique, time-based codes generated from JWT tokens.
- Instant Verification: Verify entry credentials at the reception desk in a fraction of a second.
- Anti-Fraud: Single-use tokens eliminate the risk of unauthorized access sharing.
This project was built to demonstrate enterprise-grade patterns and solve complex backend challenges:
- Clean Architecture & CQRS: The
.NETbackend utilizes theMediatRlibrary to enforce the Command Query Responsibility Segregation pattern. This separates read operations from state-mutating commands, allowing for targeted optimization and strict input validation viaFluentValidationpipelines.
π View CQRS Handler implementation - Optimistic Concurrency Control: Implemented EF Core's
RowVersionhandling. If two users attempt to book the last spot in a class simultaneously, the database rejects the conflicting transaction, ensuring capacity integrity. - Background Hosted Services (Workers): -
ScheduleGeneratorWorker: Autonomously generates upcoming class schedules and trainer slots on a rolling window without freezing the main API thread. Optimized for specific weekday triggers.
π View Background Worker logic GuardWorker: Monitors and updates expired subscription statuses autonomously.- Stripe Webhooks: Asynchronous payment fulfillment. The API listens to cryptographically signed Stripe webhooks to definitively activate user subscriptions only when the bank clears the transaction.
π View Webhook endpoint implementation - Custom React Hooks: Implementation of highly reusable hooks like
useDocumentTitleto dynamically modify the SPA's metadata state during client-side routing.
π View Custom Hook code
The following diagram shows a relational database structure (3NF) that ensures transactional consistency for subscriptions, users, and the reservation system:
Key Relationships:
- 1:N: User <-> Reservations (cascading deletes used).
- M:N: Roles <-> Permissions (secured by associative tables).
- Concurrency: Each reservation table has a
RowVersioncolumn to support optimistic locking (Optimistic Concurrency).
| Layer | Technologies & Tools |
|---|---|
| Backend | .NET 9, C# 13, ASP.NET Core Web API, MediatR, FluentValidation |
| Frontend | React 18, TypeScript, Vite, TailwindCSS, React Query, Axios |
| Database | PostgreSQL 16, Entity Framework Core (Code-First) |
| Infrastructure | Docker, Docker Compose, Nginx (Reverse Proxy & Static Server) |
| Integrations | Stripe API (Payments & Webhooks), DeepSeek API (AI Analytics) |
| Security | Custom BCrypt Hashing, JWT Bearer Authentication |
The repository is divided into two highly decoupled environments.
Click to expand the Architecture Tree
gymcore/
βββ backend/ # .NET 9 Backend
β βββ GymCore.Api/ # Presentation Layer (Controllers, Middlewares, Workers)
β βββ GymCore.Application/ # Business Logic (CQRS Handlers, Validation, Interfaces)
β βββ GymCore.Domain/ # Core Entities, Enums, Exceptions
β βββ GymCore.Infrastructure/ # DB Context, EF Core Migrations, Identity, External Services
β βββ Dockerfile # Multi-stage build for the .NET API
βββ frontend/ # Vite + React Client
β βββ src/
β β βββ components/ # Reusable UI components
β β βββ context/ # Global state management
β β βββ hooks/ # Custom React Hooks (e.g., useDocumentTitle)
β β βββ lib/ # Utility functions
β β βββ pages/ # Route views
β β βββ App.tsx # Root component
β β βββ main.tsx # Entry point
β β βββ index.css # Global styles
β βββ Dockerfile # Multi-stage build (Node builder -> Nginx server)
β βββ nginx.conf # Client-side routing configuration for SPA
βββ docker-compose.yml # E2E orchestration (DB, API, Frontend Proxy)
Run the entire E2E ecosystem locally using Docker Desktop.
- Docker & Docker Compose
- Node.js (Optional, for running frontend outside of Docker)
- Clone the repository:
git clone https://github.com/zephir-x/gymcore.git cd gymcore - Configure Environment Variables:
Create a.envfile in the root directory. Use the provided.env.exampletemplate.cp .env.example .env
- Build and Spin Up Containers:
docker compose up -d --build
- Access the application via
http://localhost:5173
To quickly evaluate the system, use the following pre-seeded credentials:
| Role | Password | Access Level | |
|---|---|---|---|
| Administrator | admin@gmail.com |
Admin123! |
Full CRUD, Analytics, Broadcasts |
| Member | member1@gmail.com |
Member123! |
Store, Bookings, Subscriptions |
| Coach | mariusz@gmail.com |
Coach123! |
Class Attendees, Calendar |
Workflow to test:
-
Log in as a Member: Navigate to the Subscriptions tab and purchase a tier using the Stripe test card (
4242 4242 4242 4242). -
Webhook Fulfillment: Return to the app. Observe that your account status changes to Active via the Stripe Webhook fulfillment mechanism.
-
Booking Engine: Navigate to Schedule and book a class. Verify that the current attendee count increments correctly.
-
Administrative Oversight: Log out, and log in as the Administrator. Cancel the class you just booked. Navigate back to the Member view to confirm the cancellation is reflected dynamically.
The application is deployed as a multi-container solution managed by a Master Nginx Reverse Proxy, allowing for side-by-side hosting of multiple projects (like GymCore and GameNest) on a single VPS.
graph TD
Client([π€ Client Browser]) -->|HTTPS / Port 443| CF[βοΈ Cloudflare]
CF -->|Proxy| MasterNginx[βοΈ Master Nginx Proxy]
subgraph Docker web-proxy Network
MasterNginx -->|Routing| GymFrontend[βοΈ React Frontend Container]
MasterNginx -->|Routing /api/*| GymApi[π .NET 9 API Container]
end
GymFrontend -.->|Axios calls| GymApi
GymApi -->|EF Core| DB[(π PostgreSQL 16)]
Stripe([π³ Stripe Service]) -.->|Async Webhooks| GymApi
- Nginx Proxy: Handles SSL/TLS termination and routes traffic to the appropriate containers based on server names.
- Internal Networking: All projects reside within a shared
web-proxybridge network. - Persistence: Docker volumes ensure database data is preserved across container restarts.
- Global Exception Handling: The API utilizes a standardized
ProblemDetailsresponse format for all unhandled exceptions, ensuring clear API communication. - Logging: Application logs are captured via Docker and can be inspected using
docker compose logs -f <service_name>.
Architected and developed by Kacper Gumulak - zephir-x.






