Organization Model Migration
Overview
The platform has been refactored from a guild-centric model to an organization-centric model. This migration enables the platform to support multiple types of organizations beyond just WoW guilds, while maintaining backward compatibility through route aliasing.
Architecture Changes
Core Data Model
Before:
Guildentity was the primary organizational unit- All relationships pointed directly to
Guild
After:
Organizationentity is the primary organizational unitWowGuildentity stores WoW-specific guild data- One-to-one relationship:
Organization→WowGuild - All domain logic references
organizationId
Database Schema
model Organization {
id String @id @default(uuid())
type String // 'guild', 'team', etc.
name String
guild WowGuild?
memberships Membership[]
roles Role[]
events Event[]
// ... other relations
}
model WowGuild {
id String @id @default(uuid())
organizationId String @unique
organization Organization @relation(fields: [organizationId], references: [id])
name String
realm String
faction String
syncSource String
// ... WoW-specific fields
}
API Changes
Endpoint Migration
All API endpoints have been updated from /guilds/:guildId/* to /organizations/:organizationId/*:
Before:
GET /api/v1/guilds/:guildId/events
POST /api/v1/guilds/:guildId/memberships
GET /api/v1/guilds/:guildId/roles
After:
GET /api/v1/organizations/:organizationId/events
POST /api/v1/organizations/:organizationId/memberships
GET /api/v1/organizations/:organizationId/roles
Backend Controllers
Controllers have been updated to use organization-scoped routes:
// membership.controller.ts
@Controller('organizations/:guildId/memberships')
export class MembershipController { ... }
// role.controller.ts
@Controller('organizations/:guildId/roles')
export class RoleController { ... }
// event.controller.ts
@Controller()
export class EventController {
@Get('organizations/:guildId/events')
findByGuild(...) { ... }
@Get('events/:id')
findOne(...) { ... }
}
Frontend Changes
Route Aliasing
The frontend maintains both /guilds/:guildId/* and /organizations/:organizationId/* routes for backward compatibility:
- User-facing routes:
/guilds/:guildId/* - Internal organization routes:
/organizations/:organizationId/* - Routes automatically redirect between formats
Parameter Aliasing Hook
// useOrganizationId.ts
export function useOrganizationId() {
const params = useParams({
from: '/organizations/$organizationId',
});
return params.organizationId;
}
This hook is used throughout components to consistently access the organization ID regardless of which route format was used.
API Integration
RTK Query slices use organizationId internally:
// organizationsApi.ts
getGuildEvents: builder.query({
query: (organizationId) => `/organizations/${organizationId}/events`,
providesTags: (result, error, organizationId) => [
{ type: 'Event', id: 'LIST' },
{ type: 'Organization', id: organizationId },
],
}),
Components alias the parameter for backward compatibility:
function EventsList() {
const organizationId = useOrganizationId();
const { data: events } = useGetGuildEventsQuery(organizationId);
// Use as guildId for prop passing
const guildId = organizationId;
return <EventCard guildId={guildId} />;
}
Test Changes
Unit Tests
All unit tests have been updated to:
- Use
organizationIdin mock data - Reference
prisma.organizationinstead ofprisma.guild - Expect
organizationIdin repository calls - Mock
organization: { findFirst }for PrismaService
E2E Tests
E2E tests have been updated to:
- Use
/organizations/:organizationId/*routes - Set
scopeandvisibilityfields on events (required for visibility filtering) - Use the updated
createTestGuildhelper which creates bothOrganizationandWowGuild
Example:
// event.e2e-spec.ts
await prisma.event.create({
data: {
organizationId: guild.id,
type: 'raid',
title: 'Raid Night',
scheduledAt: new Date('2026-03-01T19:00:00Z'),
createdBy: testUserId,
status: 'published',
scope: 'guild', // Required
visibility: 'public', // Required
},
});
const response = await request(app.getHttpServer())
.get(`/organizations/${guild.id}/events`)
.expect(200);
Migration Benefits
- Extensibility: Platform can now support multiple organization types
- Type Safety: TypeScript enforces correct usage of
organizationIdvsguildId - Separation of Concerns: WoW-specific logic isolated in
WowGuildentity - Backward Compatibility: Frontend routes continue to work with
/guilds/...URLs - Clean Architecture: Domain logic references generic organizations, not guild-specific data
Future Work
- Add support for additional organization types (teams, communities)
- Implement organization-type-specific features through polymorphism
- Consider adding organization settings that vary by type
- Backend route aliasing for
/guilds/...endpoints (currently frontend-only)
Testing Status
- ✅ All unit tests passing (API: 257 tests, Web: 42 tests)
- ✅ All e2e tests passing (48 tests across 4 suites)
- ✅ TypeScript compilation successful
- ✅ No linter errors