Skip to main content

Routing Architecture Guidelines

🚨 CRITICAL: Unified Route Pattern

NEVER create separate /system/ routes. All routes must follow the unified /$orgSlug/* pattern.

❌ FORBIDDEN Patterns

// ❌ NEVER DO THIS - Separate system route tree
src/routes/system/
src/routes/system/organizations/
src/routes/system/users/
src/routes/system/dashboard/

// ❌ NEVER DO THIS - Hardcoded system routes
<Link to="/system/users">
navigate({ to: "/system/dashboard" })

✅ CORRECT Patterns

Route Structure

src/routes/$orgSlug/
├── index.tsx // Main dashboard (system OR org)
├── organizations.tsx // Organizations management
├── users.tsx // Users management
├── users/
│ ├── invite.tsx // User invitations (system feature)
│ └── audit.tsx // User audit (system feature)
├── funds.tsx
├── funds/setup.tsx // Fund setup (system feature)
├── fund-snapshots.tsx // Fund snapshots (system feature)
├── audit-logs.tsx // Audit logs (system feature)
├── market-data-sync.tsx // Market data sync (system feature)
└── settings/
├── integrations.tsx // System integrations
└── market-data.tsx // Market data config

System Context Detection

// ✅ CORRECT - Detect system context in routes
function MyRouteComponent() {
const { orgSlug } = Route.useParams();
const isSystem = orgSlug === "system";

if (isSystem) {
// Show system-specific content
return <SystemDashboard />;
}

// Show regular organization content
return <OrganizationDashboard />;
}
// ✅ CORRECT - Always use unified pattern
<Link to="/$orgSlug/users">
<Link to="/$orgSlug/users/invite">
<Link to="/$orgSlug/organizations">

// ✅ CORRECT - Use navigate with unified pattern
const navigate = useNavigate();
void navigate({ to: "/$orgSlug/funds/setup" });
// ✅ CORRECT - System sections use unified pattern
const SYSTEM_SECTIONS: SidebarSection[] = [
{
title: "System Management",
icon: Settings,
links: [
{ title: "Users", to: "/$orgSlug/users" },
{ title: "User Invitations", to: "/$orgSlug/users/invite" },
{ title: "Audit Logs", to: "/$orgSlug/audit-logs" },
],
},
];

Context Detection Pattern

Route Component Pattern

import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/$orgSlug/users')({
component: UsersPage,
})

function UsersPage() {
const { orgSlug } = Route.useParams();
const isSystem = orgSlug === "system";

const trpc = useTRPC();

// Use same tRPC procedure for both contexts
const usersQuery = trpc.org.users.list.queryOptions();
const { data: users } = useQuery(usersQuery);

return (
<div>
<h1>
{isSystem ? "System Users" : "Organization Users"}
</h1>

{isSystem && (
<div className="bg-blue-50 p-4 mb-4">
System-wide user management across all organizations
</div>
)}

<UsersTable users={users} isSystem={isSystem} />
</div>
);
}

Component Props Pattern

// ✅ CORRECT - Pass context to child components
<UsersTable users={users} isSystem={isSystem} />

// Child component handles different behavior
function UsersTable({ users, isSystem }: { users: User[], isSystem: boolean }) {
if (isSystem) {
// Show system-specific columns/features
return <SystemUsersTable users={users} />;
}

return <OrganizationUsersTable users={users} />;
}

tRPC Procedure Usage

Same tRPC procedures work for both contexts:

// ✅ CORRECT - Same procedure, different data based on context
const usersQuery = trpc.org.users.list.queryOptions();

// Backend returns different data based on ctx.user.isSystem
// System users get all users across orgs
// Regular users get users from their org only

When to Use System Features

System-only features (shown when orgSlug === "system"):

  • Cross-organization management
  • System-wide user invitations
  • Audit logs across all organizations
  • System settings and integrations
  • Market data synchronization
  • Fund setup wizards

Regular features (shown for any org):

  • Organization-specific data
  • Fund management within org
  • Regular user management
  • Organization settings

Migration Checklist

When refactoring from old /system/ pattern:

  1. ✅ Move src/routes/system/*src/routes/$orgSlug/*
  2. ✅ Update all <Link to="/system/*"><Link to="/$orgSlug/*">
  3. ✅ Update all navigate({ to: "/system/*" })navigate({ to: "/$orgSlug/*" })
  4. ✅ Add const { orgSlug } = Route.useParams(); const isSystem = orgSlug === "system";
  5. ✅ Add conditional rendering: if (isSystem) { /* system content */ }
  6. ✅ Update sidebar to use SYSTEM_SECTIONS when isSystemOrg is true

Testing the Pattern

// Test that system routes work
import { render, screen } from '@testing-library/react'
import { createMemoryRouter } from '@tanstack/react-router'

test('system dashboard renders system content', async () => {
const router = createMemoryRouter([
{
path: '/$orgSlug',
component: () => {
const { orgSlug } = Route.useParams()
const isSystem = orgSlug === 'system'

return (
<div>
{isSystem ? <div>System Dashboard</div> : <div>Org Dashboard</div>}
</div>
)
}
}
], {
initialEntries: ['/system']
})

render(<RouterProvider router={router} />)
expect(screen.getByText('System Dashboard')).toBeInTheDocument()
})

Benefits of Unified Pattern

  1. Single Source of Truth: One route tree, one file structure
  2. Type Safety: TanStack Router handles type checking
  3. Consistent URLs: Same pattern for all contexts
  4. Easy Testing: One pattern to test
  5. Better UX: Clean, shareable URLs
  6. Simpler Navigation: No route conflicts or duplication

⚠️ Code Review Checklist

When reviewing PRs, check for:

  • ❌ Any /system/ hardcoded routes
  • ❌ Separate src/routes/system/ directories
  • ❌ Missing system context detection
  • ✅ All routes use /$orgSlug/* pattern
  • ✅ System context detected via orgSlug === "system"
  • ✅ Conditional rendering based on system context
  • ✅ Sidebar shows correct sections for system context