Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

01 — Product Overview (Organizations Module)

StudyLyon — multi-tenant ERP / School Management API. This package designs the Organizations module client (Flutter, forward-looking spec) against the implemented NestJS backend. All endpoints, DTO fields, schemas, events, queue routes, and wire contracts are derived directly from src/modules/organizations/**, src/modules/settings/**, src/modules/feature-flags/**, src/modules/rbac/**, src/modules/auth/**, src/infrastructure/workers/tenant-purge.worker.ts, and studylyon-blueprint/04-Modules/Organizations.md. No feature is invented; gaps are flagged in §10 and the module Assumptions & Open Questions.


1. Purpose

Organizations is the tenant root of the platform: it owns the lifecycle of every institution (creation, profile, branding, settings, feature flags, subscription, offboarding) and, together with Auth's register, provisions the tenant's first administrator. It is the foundation every other module's data hangs off — every business document carries the tenant's tenantId (slug) and inherits its configuration (timezone, currency, academic year, attendance rules, theme, feature toggles).

ResponsibilitySource
Tenant (institution) creation + provisioning (default roles seeded)organizations.service.ts:37 create()
Slug + domain uniqueness (platform-wide, not tenant-scoped)organizations.service.ts:40-50; organization.schema.ts:32-51
Org profile (contact, address, timezone, currency, academic year, logo)organizations.schema.ts:28-155
Org embedded settings (attendance rules, academic, theme)update-organization-settings.dto.ts:4-30; organizations.controller.ts:56-72
Org feature-flag map (metadata.featureFlags)organizations.controller.ts:74-89; organizations.service.ts:139-154
Standalone key/value settings per tenant (upsert, groups)settings.controller.ts:24-53; setting.schema.ts:7-38
Standalone feature flags per tenant (enabled/label/module)feature-flags.controller.ts:23-58; feature-flag.schema.ts:7-27
Subscription plan/status + lifecycle statusorganization.schema.ts:6-25, 82-127
Tenant offboarding: soft delete + async purge (30-day retention)organizations.service.ts:156-159; tenant-purge.worker.ts:15-47
First admin: POST /auth/register binds user to tenant + org_adminauth.service.ts:54-121; register.dto.ts:27-29
Domain event OrganizationCreated → in-app notification joborganizations.service.ts:59-66; event-queue-map.ts:13

2. Business goals

GoalMeasureSource
Tenant provisioning in one call, ready for first loginPOST /organizations returns org; POST /auth/register returns usable JWT pairorganizations.service.ts:37-68; auth.service.ts:54-121
Platform-wide uniqueness of identity handlesslug + domain unique across all tenants → 409 DUPLICATE_RESOURCEorganizations.service.ts:40-50,107-122
Safe offboardingsoft delete immediate, hard purge after 30 days, idempotent workertenant-purge.worker.ts:15,26-47
Config without hot-document writeskey/value settings + feature_flags collections, upsert semanticssetting.schema.ts:38; feature-flag.schema.ts:26
Tenant-configurable UXorg branding + settings theme drive client primary override ((proposed) wire-up)organization.schema.ts:114-120; update-organization-settings.dto.ts:26-29
No cross-tenant data leakageJWT tenantId claim; repositories inject scope; 403/404 never leakbase.repository.ts:20-30; MULTI_TENANCY.md §4

3. User goals

  • Super Admin (platform): create tenants, review all tenants, suspend/delete offboarding tenants; never operate inside a tenant's data.
  • Org Admin: see and maintain the institution's identity (name, slug, domain, contact, address, logo), timezone/currency/academic year, branding, settings groups, and feature flags from one place; invite a co-admin.
  • Invited admin: accept invitation, log in, and inherit the same org-admin surface.
  • Platform Ops: understand purge timing (30 days) and confirm offboarding consequences.

4. Stakeholders

Platform operator (SaaS vendor), Super Admin, Org Admins (multi-institution customers), incoming admins, support staff (slug/domain conflicts, purge complaints), billing (subscription plan/status), audit & security (soft-delete, isolation), engineering/design/QA consuming these docs.

5. Why this exists

Every school/coaching on the platform is a tenant. Without Organizations, there is no tenant to scope anything to: auth registers users into a tenant, RBAC seeds roles per tenant, settings/feature-flags are keyed by tenantId. This module is the first surface a customer touches (Super Admin provisioning) and the first place misconfiguration (wrong timezone, wrong working days) damages the whole institution's daily operation.

6. Dependencies (module + platform)

DependencyRoleStatus
Auth (register/login)binds first admin to tenant, org_admin roleimplemented
RBAC (seedDefaults, members)seeds DEFAULT_ROLES per tenant at org creationimplemented (organizations.service.ts:58)
Settings modulestandalone key/value config surfaceimplemented
Feature-flags modulestandalone toggle surfaceimplemented
BullMQ TENANT_PURGE queuepurge worker (30-day retention)queue + worker implemented; enqueue (planned) — see OQ-5
Storage (STORAGE_PROVIDER)logo upload → logoFileId / branding.logointerface exists; providers (planned)
EventBuseventQueueMapOrganizationCreatedin-app/org-created-notificationimplemented (event-queue-map.ts:13)
Swaggerendpoint documentationimplemented (main.ts:61-63)

7. Success metrics

  • Tenant provisioning (org + first admin login) < 15 min (00-shared/01 §2).
  • POST /organizations p95 < 300 ms (org insert + role seeding).
  • Zero slug/domain duplicates reach the DB (pre-check + unique index).
  • Offboarding: soft-delete effective immediately; purge job idempotent; nothing purged before 30 days.
  • No 5xx on settings/flag bulk saves; settings + feature-flags upsert always idempotent.
  • Cross-tenant reads structurally impossible (repository scope); tenant-list surface gated to platform — see OQ-4.

8. Edge cases

Edge caseBehaviourSource
Slug collision (auto or explicit)409 DUPLICATE_RESOURCE with the conflicting slugorganizations.service.ts:40-44
Domain collision with another org409; update path checks existing.id !== idorganizations.service.ts:46-50,115-122
No slug sentderived from name via slugify (lowercase, strip, hyphenate)organizations.service.ts:38,161-169
Create never takes a statusforced ONBOARDING; activation via later PATCHorganizations.service.ts:55
PATCH :id/settings sends partial objectfull-replace $set {settings} — missing groups are wiped; client must send all groupsorganizations.service.ts:134
PATCH :id/feature-flags sends partial mapfull-replace $set metadata.featureFlags — missing keys deletedorganizations.service.ts:150
Invalid ObjectId in :idCastError → 400 VALIDATION_ERROR "Invalid resource identifier."http-exception.filter.ts:47-55,92
Delete of unknown/soft-deleted id404 RESOURCE_NOT_FOUNDorganizations.service.ts:158
Register with email already used409 "User with this email already exists."auth.service.ts:56-58
Register with unknown tenantIdaccepted — no org-existence check in register (see OQ-2)auth.service.ts:60-69
Soft-deleted orgexcluded from all queries (isDeleted:false filter)organizations.repository.ts:15-19
Purge runs while new soft-deletes existonly deletes docs deletedAt older than 30-day cutoff; idempotenttenant-purge.worker.ts:32-42
Cross-tenant id access403/404, never leak existence07_API_Conventions.md §6

9. Assumptions

  1. Client scope flag (matches 00-shared/01 §9): native mobile apps are out of Phase 1 (PRODUCT_REQUIREMENTS_DOCUMENT.md:144); these docs are forward-looking full client specs against the implemented API. Module-specific "forward-looking" marks apply to push/QR/deep links.
  2. Organization surface is admin-only (/organization destination, 00-shared/05 §2).
  3. The tenant identifier in JWT and every tenant-scoped collection is the org slug string; the org document's _id is a separate ObjectId (see OQ-1 for the self-view gap).
  4. Client-side validation (lengths, hex color regex, working-day ranges) is (proposed) — the backend DTOs impose no min/max beyond @IsEmail, enums, and object types.
  5. (planned) items come from studylyon-blueprint/04-Modules/Organizations.md and docs/IMPLEMENTATION_PLAN.md (R2 provider, rate-limit tiers, institution type).
  6. Analytics events are (proposed) contract only (10_QA_Baseline.md §8).

10. Assumptions & Open Questions

#ItemStatus / Impact
OQ-1No self-org endpoint. GET /organizations/:id needs the org _id, but JWT carries only the slug (auth.service.ts:461). Client cannot fetch its own org profile without a cross-tenant listing. Resolution (planned): GET /organizations/me (or slug-resolved lookup); until then client may use GET /organizations?q=<slug> as stopgap (see OQ-4 risk).Org Overview screen
OQ-2register does not verify the org exists or is active (auth.service.ts:60-69). Any slug registers an admin. Client should surface "tenant not found" guidance; server validation (planned).Registration journey
OQ-3OrganizationUpdated event interface exists (organization-created.event.ts:10-16) but is never emitted by update(). No audit signal for profile changes today.Audit trail
OQ-4OrganizationsRepository is not tenant-scoped (organizations.repository.ts:15-19 — only isDeleted filter) and the controller has no JWT/RBAC guard (organizations.controller.ts:1-90). A tenant admin calling GET /organizations today sees every tenant (slug + domain). Client must gate the tenants list + tenant detail to platform admin only; server guard/scope (planned).Isolation, tenants list
OQ-5Blueprint: "Offboarding triggers TenantPurgeJob" (Organizations.md:61) — but remove() only soft-deletes; nothing enqueues QUEUE.TENANT_PURGE. Worker purges all soft-deleted docs ≥30 days across all models. Enqueue + per-tenant purge payload (planned).Offboarding
OQ-6Blueprint lists a separate organization_settings collection (Organizations.md:7); code keeps settings embedded in organizations.settings and a generic settings collection. Two surfaces exist; sync between them is unspecified.Settings UI
OQ-7No organization.create permission in permissions.constants.ts:1-97 — create is platform-only by role design; client hardcodes it as such.Tenants list

11. Glossary

TermMeaningSource
TenantOne institution; identified by its slug in JWT + every tenantId fieldMULTI_TENANCY.md §1
SlugUnique lowercase URL-safe handle; auto-derived from nameorganizations.service.ts:161-169
Organization statusonboarding / active / inactive / suspendedorganization.schema.ts:20-25
Subscription plan/statusfree/basic/premium/enterprise; active/inactive/suspended/trialorganization.schema.ts:6-18
Settings (embedded)attendance {gracePeriod,lateThreshold,halfDayThreshold,workingDays} / academic {gradingScale,passingPercentage} / theme {primaryColor,secondaryColor}update-organization-settings.dto.ts:4-30
Feature flags (map)metadata.featureFlags: Record<string,boolean> — tenant-level togglesorganizations.service.ts:139-154
Settings (collection){tenantId,key,value,group} unique per tenant+key; groups academic/attendance/grading/notification/theme/generalsetting.schema.ts:7-38
PurgeWorker deleting all isDeleted docs older than 30 days (all models)tenant-purge.worker.ts:15,32-42
Envelope{success,message,data,meta?,timestamp,requestId}07_API_Conventions.md §2-3
Permissionresource.action strings incl. organization.*, settings.*, feature-flags.*permissions.constants.ts:2-5,75-80