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
| Context | Details |
|---|---|
| Aggregate | Gig::Job (root) |
| Layer | Posting |
| Upstream dependencies | Org::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 dependents | Gig::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.
| From | To | Trigger | Notes |
|---|---|---|---|
| (new) | active | UC-1: Job created | Default status on creation |
active | archived | UC-3: Employer archives | No new Shifts can be added. Existing open Shifts are not affected. |
archived | active | UC-4: Employer reactivates | Job returns to active state, new Shifts can be added again. |
Use Cases
| ID | Use Case | Trigger | Actor |
|---|---|---|---|
| UC-1 | Create a new Job for a company | Employer needs a new type of gig worker | Employer |
| UC-2 | Edit a Job's details | Description updated, outlet changed, etc. | Employer |
| UC-3 | Archive a Job that is no longer needed | Employer no longer needs this type of worker | Employer |
| UC-4 | Reactivate a previously archived Job | Employer needs this type of worker again | Employer |
| UC-5 | View list of Jobs for a company | Employer managing their job catalog | Employer |
| UC-6 | View Job detail with upcoming Shifts | Employer reviewing a specific job and its shifts | Employer |
| UC-7 | Admin views all Jobs across companies | Ops reviewing platform job catalog | Admin |
UC-1: Create a new Job for a company
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Employer needs a new type of gig worker |
Preconditions:
Org::Companyexists and is active- Employer has permission to create jobs for this company
System Behavior:
- Employer selects an
Org::Outlet(which branch the work is at) - Employer selects an
Org::JobRole(what role is needed — e.g., "Service Crew", "Kitchen Helper") - Employer enters additional details: description, requirements
- System creates the Job with
status: active - Job is immediately available for adding Shifts
Business Rules:
- A Job must belong to exactly one
Org::Company - A Job must reference an
Org::Outletand anOrg::JobRole - Pay rates are NOT set on the Job — they are resolved from
Gig::PayRatewhen 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
openShift does
Postconditions:
- Job exists with
status: active - No Shifts exist yet — employer adds them separately
UC-2: Edit a Job's details
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Description updated, outlet changed, etc. |
Preconditions:
- Job exists
System Behavior:
- Employer modifies job fields: title, description, requirements
- System validates inputs
Listings::Jobis 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_idororg_job_role_idis allowed but affects whichGig::PayRateentries are used for future Shifts — existing Shifts keep their resolved rate - Edits are allowed regardless of Job status (
activeorarchived)
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
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Employer no longer needs this type of worker |
Preconditions:
- Job exists and
status: active
System Behavior:
- Employer archives the Job
- 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
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Employer needs this type of worker again |
Preconditions:
- Job exists and
status: archived
System Behavior:
- Employer reactivates the Job
- 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
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Employer managing their job catalog |
Preconditions:
- Employer belongs to an
Org::Company
System Behavior:
- Employer navigates to their job list
- System displays all Jobs for the company, showing: title, outlet, role, status, number of upcoming open Shifts
- Default view shows
activeJobs. Employer can toggle to seearchivedJobs.
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
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Employer reviewing a specific job and its shifts |
Preconditions:
- Job exists
System Behavior:
- Employer selects a Job from the list
- System displays Job details (title, description, requirements, outlet, role, current pay rates from
Gig::PayRate) - System displays all Shifts under this Job, grouped by status:
- Upcoming:
draft,pending_approval,openShifts - In progress:
active,pending_verificationShifts - Completed:
completedShifts (recent) - Other:
cancelled,expiredShifts
- Upcoming:
- 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
| Field | Details |
|---|---|
| Actor | Identities::Admin |
| Trigger | Ops reviewing platform job catalog |
Preconditions:
- None
System Behavior:
- Admin navigates to the Jobs admin page
- System displays all Jobs across all companies
- 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
- A Job must belong to exactly one
Org::Company— this cannot change after creation - A Job must reference an
Org::Outletand anOrg::JobRole - Jobs are never hard-deleted — only archived via
status - An archived Job cannot have new Shifts added to it
- Archiving a Job does not affect its existing Shifts
- A Job does NOT store pay rates — rates are resolved from
Gig::PayRateat Shift creation time - A Job's status is independent of its Shifts' statuses — Job status is managed directly by the employer, not derived
Model Interactions
| Related Model | Relationship | Interaction |
|---|---|---|
Org::Company | Job belongs_to Company | Every Job is owned by one company. The company determines which employers can manage the Job. |
Org::Outlet | Job belongs_to Outlet | The physical branch/location where the work happens. Determines which Gig::PayRate entries apply for rate resolution. |
Org::JobRole | Job belongs_to JobRole | The role definition (e.g., "Service Crew"). Combined with outlet, determines which Gig::PayRate entries apply. |
Gig::PayRate | Indirect — via outlet + role | Job 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::Shift | Job has_many Shifts | Shifts are the time windows added to a Job. All fulfilment and rate resolution happens at the Shift level. |
Listings::Job | Job synced to Listings::Job | The marketplace listing is synced from the Job and its open Shifts. When a Shift becomes open, the listing is updated. |
Org::UserProfile | Employer creates and manages Jobs | Employers create, edit, and archive Jobs. Permission is determined by the employer's relationship to the company. |
Schema Gaps
| Gap | Impact | Suggested Resolution |
|---|---|---|
Current DBML gig_jobs has no description or requirements columns | Cannot store job details that talent see on the listing | Add description text, requirements text columns |
Current DBML gig_jobs has no org_outlet_id column | Cannot link Job to a specific outlet/branch | Add org_outlet_id bigint [ref: > org_outlets.id, not null] |
Current DBML gig_jobs has no org_job_role_id column | Cannot link Job to the company's role taxonomy | Add org_job_role_id bigint [ref: > org_job_roles.id, not null] |
No uuid on gig_jobs | Inconsistent with other domain models that use UUIDs for external references | Add uuid string [unique, not null] |
No created_by / updated_by on gig_jobs | No audit trail for who created or last modified the Job | Add FK references to org_user_profiles or identities_users |