Xircuit Blog

Product updates, developer logs, and news from behind the scenes at Xircuit.

One Platform, Seven Industries: Multi-Tenancy from Day One cover image

One Platform, Seven Industries: Multi-Tenancy from Day One


One Platform, Seven Industries: Multi-Tenancy from Day One

Reception area of a modern studio Photo: Daria Nepriakhina · StockSnap (CC0 1.0), via Openverse

When we started with Xircuit, there was a temptation in the room that almost every software project knows: build for the first customer and figure out everything else later. A gym has very concrete wishes, and it would have been convenient to pour exactly those wishes into tables and menus. Except that would have maneuvered us into a corner that's hard to get back out of later. A physiotherapy practice, a club, a spa — at their core they all need the same thing, but they speak a different language and operate by different rules.

That's why, from the very first line, we didn't build a studio, but a multi-tenant platform that carries many studios, practices, and clubs side by side. This week I describe how this multi-tenancy is structured — and why the three unassuming building blocks of organization, role, and membership carry the entire weight of the system.

One Platform, Seven Languages

Every organization in Xircuit belongs to exactly one of seven industries. We call them Tiers internally, and they're more than a label: a Tier determines how the interface speaks to the people who belong to an organization. The same data model, the same member list, the same detail page — but in the gym it says "Member," in the practice it says "Patient," and in a coaching context it says "Client."

That sounds cosmetic, but it's a deliberate architectural decision. We could have built a separate application for each industry, with its own vocabulary and its own data storage. The result would have been seven codebases slowly drifting apart, and every bug fix would have had to be made seven times. Instead, there's exactly one model of a person belonging to an organization, and the Tier only determines which terms and which defaults get layered on top. Language is a surface layer, not a fracture in the foundation.

This distinction between "what it really is" and "what we call it here" runs through the entire product. An appointment stays an appointment, whether it's a styling slot at the salon or a treatment at the practice. The core is shared; the facade is industry-specific.

Roles Belong to the Organization, Not the User

The second load-bearing decision concerns roles. What someone is allowed to do in Xircuit doesn't follow from a global property of the person, but from their role within a specific organization. A GymManager, a Trainer, a PracticeAdmin, a plain Member — none of these are labels that stick to a user account across the board; they're roles bound to the combination of person and organization.

An example makes the difference tangible. The same person can be a Trainer at one studio and simply a member at another. They can be a PracticeAdmin at their own practice and just a regular member at the club next door. It's always the same identity — the stable sign-in from Auth0 I wrote about last week — but the rights depend on context. So when the active organization changes, it isn't the person who changes, but the bundle of what they're allowed to do here.

A diverse group of people Photo: Startup Stock Photos · StockSnap (CC0 1.0), via Openverse

Had we assigned roles globally, this side-by-side coexistence would have been impossible without creating a new account for every constellation. People would have had to juggle multiple logins just because they operate under multiple roles. By tying the role to the membership rather than to the user, identity stays one and the same, and permissions remain cleanly separated per organization.

Authorization in a Single Place

When rights are context-dependent, the question "is this person allowed to do that right now" quickly gets tricky. That's exactly why we didn't scatter authorization across the code, but bundled it centrally. Every check runs through the same place, instead of each endpoint having its own, slightly different variant of the same logic. This is less about convenience than a security principle: scattered permission checks are where gaps form, because sooner or later, when a rule changes, one copy gets forgotten.

One detail I pay particular attention to is logging denials. We have a small AuthLog deny helper that every rejected request runs through, and it's recorded in a traceable way — which rule applied and in what context. A silent denial is a nightmare in operation: someone can't get to something they should actually be able to see, and nobody can reconstruct why. By consistently voicing and recording the "no" in one single place, a vague "this doesn't work" turns into a concrete, diagnosable line.

What still matters here is last week's rule: we log the flow, not the content. The deny helper records that and why a check failed, but never personal data. The goal is to make the path traceable without exposing the people who walked it.

Membership Management as the Shared Foundation

All of this comes down to a point that's easy to overlook precisely because it's so fundamental: membership management is the shared base that everything else later builds on. Appointments, health data, billing, access — not one of these topics stands on its own. They all need the answer to the same question: which person belongs to which organization, in what role?

An appointment is the arrangement between a member and an organization. A health measurement belongs to a member and is visible only to certain roles within that same organization. An invoice goes to a member; a door entry check verifies whether that member is currently authorized. If membership as a foundation isn't right, everything built on top of it topples. That's why we put a great deal of care into this layer before tackling even one of the areas built on top of it.

You only notice the foundation once it's missing. As long as it holds, it stays invisible — and that's exactly the goal.

Realistic Data Across All Seven Tiers

A multi-tenant platform has an ugly testing problem: if you only ever develop against one empty studio, you never actually see the other six industries in action. Bugs that only occur in the physio practice, or only in the club, then only get discovered once a real customer stumbles over them. We wanted to avoid that.

That's why our seed data builds realistic example organizations across all seven Tiers. When the system starts up, a gym, a practice, a club, a spa, a salon, and the remaining industries appear with plausible members, roles, and relationships — no placeholders like "Test Test," but records that resemble a real organization. That lets every screen be checked against real data, and you see immediately whether the industry-specific language holds up, whether roles resolve correctly, and whether centralized authorization delivers the expected result.

The side effect is a better development feel. Anyone building a new feature doesn't click through a sterile demo account, but through seven believable worlds side by side. Anything that looks odd in a particular industry stands out early — long before it hits anyone in production.

What's Next?

Next week: a logging and audit standard that makes errors diagnosable without ever logging personal data.


Comments

No comments yet. Be the first to share your thoughts!

Leave a Comment