6.3 KiB
organization
Purpose
Defines the organization model: organizations, members, workspaces, roles, and scoped role assignments.
Requirements
Requirement: Organizations table
The system SHALL maintain an organizations table with fields: org_id (UUIDv7 primary key, DEFAULT uuidv7()), name (VARCHAR(255), NOT NULL), slug (VARCHAR(100), UNIQUE, NOT NULL), org_type (VARCHAR(20), NOT NULL, FK → organization.org_types(org_type)), owner_person_id (UUID, FK → persons, NOT NULL), status (VARCHAR(20), NOT NULL, DEFAULT 'active'), created_at (TIMESTAMPTZ), updated_at (TIMESTAMPTZ).
Scenario: Organization slug uniqueness
- WHEN an attempt is made to create an organization with a slug that already exists
- THEN the database SHALL reject the insert with a unique constraint violation
Scenario: Organization has an owner
- WHEN an organization is created
- THEN it SHALL reference a
personsrecord asowner_person_id
Scenario: Organization type referential integrity
- WHEN an attempt is made to create an organization with an
org_typethat does not exist inorg_types - THEN the database SHALL reject the insert with a foreign key constraint violation
Requirement: Organization members
The system SHALL maintain an org_members table with fields: org_member_id (UUIDv7 primary key), org_id (UUID, FK → organizations, NOT NULL), person_id (UUID, FK → persons, NOT NULL), role_id (UUID, FK → roles, NOT NULL), status (VARCHAR(20), NOT NULL, DEFAULT 'active'), joined_at (TIMESTAMPTZ, NOT NULL, DEFAULT NOW()), removed_at (TIMESTAMPTZ), created_at (TIMESTAMPTZ), updated_at (TIMESTAMPTZ). The combination of org_id and person_id SHALL be unique (a person can be a member of an org only once).
Scenario: Person joins organization
- WHEN an
org_membersrecord is created linking a person to an organization with a role - THEN the person SHALL be considered a member of that organization with the permissions granted by the assigned role
Scenario: Duplicate membership prevented
- WHEN an attempt is made to add a person to an organization they already belong to
- THEN the database SHALL reject the insert with a unique constraint violation on
(org_id, person_id)
Requirement: Workspaces
The system SHALL maintain a workspaces table with fields: workspace_id (UUIDv7 primary key), org_id (UUID, FK → organizations, NOT NULL), name (VARCHAR(255), NOT NULL), slug (VARCHAR(100), NOT NULL), description (TEXT), status (VARCHAR(20), NOT NULL, DEFAULT 'active'), created_at (TIMESTAMPTZ), updated_at (TIMESTAMPTZ). The combination of org_id and slug SHALL be unique (workspace slugs are unique within an organization).
Scenario: Workspace belongs to organization
- WHEN a workspace is created
- THEN it SHALL reference exactly one organization via
org_id
Scenario: Workspace slug unique within org
- WHEN an attempt is made to create a workspace with a slug that already exists within the same organization
- THEN the database SHALL reject the insert with a unique constraint violation on
(org_id, slug)
Requirement: Roles with flat permission model
The system SHALL maintain a roles table with fields: role_id (UUIDv7 primary key), org_id (UUID, FK → organizations, nullable), role_name (VARCHAR(100), NOT NULL), display_name (VARCHAR(255), NOT NULL), description (TEXT), is_system (BOOLEAN, NOT NULL, DEFAULT FALSE), permissions (TEXT[], NOT NULL), created_at (TIMESTAMPTZ), updated_at (TIMESTAMPTZ). System roles (is_system = TRUE) have org_id = NULL. Custom roles have org_id set.
Scenario: System roles are seeded
- WHEN the organization module's migration runs
- THEN the following system roles SHALL exist:
owner,admin,member,billing,viewer,platform_admin - AND each role SHALL have the permission arrays defined in the design's role specification
- AND all system roles SHALL have
is_system = TRUEandorg_id = NULL
Scenario: Permission format
- WHEN a role's permissions are queried
- THEN each permission SHALL be a string in
resource:actionformat (e.g.,billing:manage,workspace:delete)
Requirement: Scoped role assignments
The system SHALL maintain a role_assignments table with fields: assignment_id (UUIDv7 primary key), role_id (UUID, FK → roles, NOT NULL), person_id (UUID, FK → persons, NOT NULL), org_id (UUID, FK → organizations, NOT NULL), scope_type (VARCHAR(20), NOT NULL — values: organization, workspace, pool), scope_id (UUID, NOT NULL), created_at (TIMESTAMPTZ), updated_at (TIMESTAMPTZ). The combination of role_id, person_id, org_id, scope_type, and scope_id SHALL be unique.
Scenario: Organization-scoped assignment
- WHEN a role assignment is created with
scope_type = 'organization'andscope_idequal to anorg_id - THEN the person SHALL have that role's permissions for the entire organization
Scenario: Workspace-scoped assignment
- WHEN a role assignment is created with
scope_type = 'workspace'andscope_idequal to aworkspace_id - THEN the person SHALL have that role's permissions for that specific workspace only
Requirement: Organization module owns its schema
The organization module SHALL own its database schema via migrations in internal/organization/migrations/. The organization module's sqlc configuration SHALL read both identity and organization migrations to resolve cross-module FK references. It SHALL generate into the internal/organization/ Go package.
Scenario: Organization migration creates tables
- WHEN the organization module's migration runs (after identity migration)
- THEN the
organizations,org_members,workspaces,roles, androle_assignmentstables SHALL exist with all specified columns, constraints, and indexes
Requirement: Updated at triggers
The system SHALL automatically update the updated_at timestamp on organizations, org_members, workspaces, roles, and role_assignments rows whenever they are modified.
Scenario: Organization record updated
- WHEN any field on an
organizationsrecord is modified - THEN the
updated_atfield SHALL be set to the current timestamp via a database trigger