Skip to content
← All projects

Deployed application system

MartialXPro

A tournament management platform for martial arts organisations, documented here through its data model, application workflows and current deployment path.

martialxpro.com ↗
Role
Co-Founder & Technical Architect
Period
2026 - Present
Scope
System design · Data · Linux deployment
Status
Live in production

The problem

The product is designed around a tournament workflow that was split across spreadsheets, message groups and paper: registration; classification by age, weight and belt; fixture generation; result tracking; certificates; and ID cards.

The platform separates responsibilities across three access roles: admin, organiser and player.

System architecture

The current deployment uses one Ubuntu host: Nginx accepts requests and proxies them to a PM2-supervised Node.js and Express API, which reads and writes PostgreSQL through Prisma. This keeps the operational surface small; host-level redundancy and horizontal scaling remain future work.

UBUNTU SERVERClientorganiseradmin · playerHTTPSNginxreverse proxyTLS · static · routingproxy_passExpress APIroutes + middlewareJWT → RBAC middlewaresupervised by PM2PrismaPostgreSQLrelational schemamigrationsPM2 supervision · environment-based config · Prisma migrations
Current single-host request path for the production deployment.

Runtime configuration comes from environment variables, allowing the same code to be deployed without environment-specific edits.

Data model

The core model is relational: an organisation owns tournaments; tournaments contain categories; and registrations connect players to the categories they enter. Fixture generation, reports and analytics all depend on those same relationships.

MartialXPro relational data modelAn organisation owns tournaments. Tournaments contain categories. Registrations connect players to categories. Fixture generation, reports and analytics depend on those relationships.CORE RELATIONSHIPSOrganisationownerownsTournamenteventcontainsCategoryage · weight · beltcategoryRegistrationjoin recordplayerPlayerentrantDOWNSTREAM FEATURESFixture generationReportsAnalytics
Core relational model used by registration, fixture generation, reporting and analytics.

Schema changes are managed as Prisma migrations, versioned alongside the code, so the database state is reproducible rather than hand-edited.

Authentication and access control

Sessions are JWT-based, and authorisation is role-based access control across three roles:

  • Admin
    Platform-level control across organisations.
  • Organiser
    Runs tournaments for their own organisation.
  • Player
    Registers and competes.

Route middleware resolves role and ownership context before a handler runs. Centralising that logic keeps route declarations consistent and avoids duplicating checks inside individual handlers.

What the platform does

Implemented in the current product:

Organisation managementTournament managementPlayer registrationTournament registrationCategory managementAutomated fixture generationCertificate generationPlayer ID cardsReportsAnalytics dashboard

Designed; implementation in progress:

Subscription plansPayment workflow

Deployment and operations

I use PM2 process logs and Nginx access and error logs to diagnose application, proxy and routing failures on the host. PM2 is configured to restart the API after process failure and host reboot.

The scale-out plan covers EC2 in a custom VPC, IAM-scoped access, S3 for generated documents and CloudWatch for visibility. The AWS case study documents the labs behind that plan.

What broke, and what it taught me

These failures changed how I sequence migrations, centralise access checks, set file ownership and diagnose the deployed service.

  1. Prisma migrations drifting from production

    Schema changes that applied cleanly in development did not always land cleanly against a database holding real tournament data. Resolving migration state without dropping and reseeding the production database changed how I sequence schema changes and was the single most instructive part of the project.

  2. Authentication and role resolution edge cases

    With three role types and organisations owning their own tournaments, 'is this user allowed to do this' is not a single check. Token handling, role resolution and ownership checks all had to agree, and the failures were the quiet kind that return the wrong data rather than an error.

  3. PostgreSQL behaviour underneath the ORM

    The ORM hides the database until it doesn't. Connection handling and query behaviour on relational reads had to be understood at the SQL level, not just the Prisma level, before some issues made sense.

  4. Linux permissions between deploy user and process user

    The account that deploys the code and the account that runs it are not the same, and file ownership, log directories and environment file access all have to reflect that. Classic Linux administration, and it only surfaces in production.

  5. Nginx reverse proxy configuration

    Getting upstream routing, forwarded headers and the split between proxied API traffic and static content correct, so the application sees the request it expects rather than the one Nginx rewrote.

  6. Debugging against a live server

    Some failures appeared only on the deployed host. PM2 and Nginx logs, process state, file ownership and proxy behaviour became the primary evidence for isolating them.

Use the up and down arrow keys to choose a result, Enter to open it, and Escape to close search.