Skip to main content

Gig::Job

Purpose

A Job is a long-lived definition of a manpower need — it describes the role, outlet, description, and requirements for a type of work that an employer regularly needs filled. Jobs are not one-time postings. Employers create a Job once and add new Shifts to it over time as they need workers.

A Job acts as a container that holds the stable attributes (what the work is, where it is) while Shifts hold the variable attributes (when the work happens, how many workers are needed, the resolved hourly rate). This separation eliminates the legacy pattern where employers had to re-enter the same job details every time they needed a new shift.

A Job does not own pay rates directly. Pay rates are configured in the Org domain via Gig::PayRate (per company + outlet + role + conditions). When a Shift is created under a Job, the system resolves the applicable rate from Gig::PayRate and stores it on the Shift. This avoids the "stale snapshot" problem where copied rate cards diverge from the source of truth over time (see Pay Rate Patterns for the data that drove this decision).

Model Context

ContextDetails
AggregateGig::Job (root)
LayerPosting
Upstream dependenciesOrg::Company (which company owns this job), Org::Outlet (which branch the work is at), Org::JobRole (what role is needed), Gig::PayRate (rate card used to resolve shift rates)
Downstream dependentsGig::Shift (time windows added to this job), Listings::Job (marketplace listing synced from this job and its open shifts)

State Machine

Job uses a simple status enum rather than a complex lifecycle. All fulfilment lifecycle tracking happens on Gig::Shift, not on Job.

FromToTriggerNotes
(new)activeUC-1: Job createdDefault status on creation
activearchivedUC-3: Employer archivesNo new Shifts can be added. Existing open Shifts are not affected.
archivedactiveUC-4: Employer reactivatesJob returns to active state, new Shifts can be added again.

Use Cases

IDUse CaseTriggerActor
UC-1Create a new Job for a companyEmployer needs a new type of gig workerEmployer
UC-2Edit a Job's detailsDescription updated, outlet changed, etc.Employer
UC-3Archive a Job that is no longer neededEmployer no longer needs this type of workerEmployer
UC-4Reactivate a previously archived JobEmployer needs this type of worker againEmployer
UC-5View list of Jobs for a companyEmployer managing their job catalogEmployer
UC-6View Job detail with upcoming ShiftsEmployer reviewing a specific job and its shiftsEmployer
UC-7Admin views all Jobs across companiesOps reviewing platform job catalogAdmin

UC-1: Create a new Job for a company

FieldDetails
ActorOrg::UserProfile (employer)
TriggerEmployer needs a new type of gig worker

Preconditions:

  • Org::Company exists and is active
  • Employer has permission to create jobs for this company

System Behavior:

  1. Employer selects an Org::Outlet (which branch the work is at)
  2. Employer selects an Org::JobRole (what role is needed — e.g., "Service Crew", "Kitchen Helper")
  3. Employer enters additional details: description, requirements
  4. System creates the Job with status: active
  5. Job is immediately available for adding Shifts

Business Rules:

  • A Job must belong to exactly one Org::Company
  • A Job must reference an Org::Outlet and an Org::JobRole
  • Pay rates are NOT set on the Job — they are resolved from Gig::PayRate when a Shift is created
  • The same company can have multiple active Jobs (e.g., "Service Crew at Tampines", "Kitchen Helper at Orchard")
  • Creating a Job does not make anything visible on the marketplace — only adding an open Shift does

Postconditions:

  • Job exists with status: active
  • No Shifts exist yet — employer adds them separately

UC-2: Edit a Job's details

FieldDetails
ActorOrg::UserProfile (employer)
TriggerDescription updated, outlet changed, etc.

Preconditions:

  • Job exists

System Behavior:

  1. Employer modifies job fields: title, description, requirements
  2. System validates inputs
  3. Listings::Job is updated to reflect changes (for any open Shifts under this Job)

Business Rules:

  • Editable fields: title, description, requirements
  • Immutable fields: org_company_id — a Job cannot be transferred between companies
  • Changing org_outlet_id or org_job_role_id is allowed but affects which Gig::PayRate entries are used for future Shifts — existing Shifts keep their resolved rate
  • Edits are allowed regardless of Job status (active or archived)

Postconditions:

  • Job reflects updated values
  • Marketplace listing updated for any open Shifts under this Job

UC-3: Archive a Job that is no longer needed

FieldDetails
ActorOrg::UserProfile (employer)
TriggerEmployer no longer needs this type of worker

Preconditions:

  • Job exists and status: active

System Behavior:

  1. Employer archives the Job
  2. System sets status: archived

Business Rules:

  • Archiving does NOT affect existing Shifts — open, active, or in-progress Shifts continue normally
  • No new Shifts can be added to an archived Job
  • The Job disappears from the employer's "active jobs" list but remains accessible in an "archived" view
  • Archiving is reversible (see UC-4)

Postconditions:

  • Job status: archived
  • Existing Shifts unaffected
  • Employer cannot add new Shifts until the Job is reactivated

UC-4: Reactivate a previously archived Job

FieldDetails
ActorOrg::UserProfile (employer)
TriggerEmployer needs this type of worker again

Preconditions:

  • Job exists and status: archived

System Behavior:

  1. Employer reactivates the Job
  2. System sets status: active

Business Rules:

  • No automatic creation of Shifts — employer must add them manually
  • All job details are preserved from before archiving

Postconditions:

  • Job status: active
  • Employer can add new Shifts again

UC-5: View list of Jobs for a company

FieldDetails
ActorOrg::UserProfile (employer)
TriggerEmployer managing their job catalog

Preconditions:

  • Employer belongs to an Org::Company

System Behavior:

  1. Employer navigates to their job list
  2. System displays all Jobs for the company, showing: title, outlet, role, status, number of upcoming open Shifts
  3. Default view shows active Jobs. Employer can toggle to see archived Jobs.

Business Rules:

  • Filterable by status (active, archived), outlet, role
  • Sortable by title, outlet, number of upcoming shifts

Postconditions:

  • Read-only operation — no data changes

UC-6: View Job detail with upcoming Shifts

FieldDetails
ActorOrg::UserProfile (employer)
TriggerEmployer reviewing a specific job and its shifts

Preconditions:

  • Job exists

System Behavior:

  1. Employer selects a Job from the list
  2. System displays Job details (title, description, requirements, outlet, role, current pay rates from Gig::PayRate)
  3. System displays all Shifts under this Job, grouped by status:
    • Upcoming: draft, pending_approval, open Shifts
    • In progress: active, pending_verification Shifts
    • Completed: completed Shifts (recent)
    • Other: cancelled, expired Shifts
  4. Each Shift shows: date/time, headcount, filled count, status

Business Rules:

  • Employer can add a new Shift from this view (navigates to Shift creation)
  • Completed and cancelled Shifts are shown for reference but are read-only

Postconditions:

  • Read-only operation — no data changes

UC-7: Admin views all Jobs across companies

FieldDetails
ActorIdentities::Admin
TriggerOps reviewing platform job catalog

Preconditions:

  • None

System Behavior:

  1. Admin navigates to the Jobs admin page
  2. System displays all Jobs across all companies
  3. Filterable by company, status, location

Business Rules:

  • Admin can see both active and archived Jobs
  • Admin cannot edit Jobs directly — they manage via the company's employer interface or through direct support

Postconditions:

  • Read-only operation — no data changes

Invariants

  1. A Job must belong to exactly one Org::Company — this cannot change after creation
  2. A Job must reference an Org::Outlet and an Org::JobRole
  3. Jobs are never hard-deleted — only archived via status
  4. An archived Job cannot have new Shifts added to it
  5. Archiving a Job does not affect its existing Shifts
  6. A Job does NOT store pay rates — rates are resolved from Gig::PayRate at Shift creation time
  7. A Job's status is independent of its Shifts' statuses — Job status is managed directly by the employer, not derived

Model Interactions

Related ModelRelationshipInteraction
Org::CompanyJob belongs_to CompanyEvery Job is owned by one company. The company determines which employers can manage the Job.
Org::OutletJob belongs_to OutletThe physical branch/location where the work happens. Determines which Gig::PayRate entries apply for rate resolution.
Org::JobRoleJob belongs_to JobRoleThe role definition (e.g., "Service Crew"). Combined with outlet, determines which Gig::PayRate entries apply.
Gig::PayRateIndirect — via outlet + roleJob does not own pay rates. When a Shift is created, the system resolves the rate from Gig::PayRate using the Job's outlet and role.
Gig::ShiftJob has_many ShiftsShifts are the time windows added to a Job. All fulfilment and rate resolution happens at the Shift level.
Listings::JobJob synced to Listings::JobThe marketplace listing is synced from the Job and its open Shifts. When a Shift becomes open, the listing is updated.
Org::UserProfileEmployer creates and manages JobsEmployers create, edit, and archive Jobs. Permission is determined by the employer's relationship to the company.

Schema Gaps

GapImpactSuggested Resolution
Current DBML gig_jobs has no description or requirements columnsCannot store job details that talent see on the listingAdd description text, requirements text columns
Current DBML gig_jobs has no org_outlet_id columnCannot link Job to a specific outlet/branchAdd org_outlet_id bigint [ref: > org_outlets.id, not null]
Current DBML gig_jobs has no org_job_role_id columnCannot link Job to the company's role taxonomyAdd org_job_role_id bigint [ref: > org_job_roles.id, not null]
No uuid on gig_jobsInconsistent with other domain models that use UUIDs for external referencesAdd uuid string [unique, not null]
No created_by / updated_by on gig_jobsNo audit trail for who created or last modified the JobAdd FK references to org_user_profiles or identities_users