[Employer Sync 5.1] Extract a shared Identities::User upsert service
TL;DR: Pull the single-user Identities::User upsert into one service (JodGig::Users::UpsertIdentitiesUserService) so four callers — talent batch, talent JIT, employer batch, and the new employer JIT (5.2) — stop drifting apart.
Context
The talent batch sync, the talent JIT, and the employer batch sync (after issue 4.2) all write Identities::User rows from a JodGig::User. Issue 5.2 adds a fourth caller — a single-employer JIT triggered from the login flow.
Problem
Each existing caller has its own hand-rolled upsert logic. They drift apart over time. The next bug fix has to be remembered four times, and forgetting once breaks one of the four paths silently.
Direction
Pull the single-user upsert into one service:
app/domains/jod_gig/users/upsert_identities_user_service.rb
JodGig::Users::UpsertIdentitiesUserService.execute(jodgig_user:, kind:) — kind is :talent or :employer. The service:
- Calls the right mapper for the kind (the shared talent mapper for
:talent; the employer-specific path from 4.2 for:employer). - Runs a single-row upsert keyed on
remote_gig_user_id. - Returns the
Identities::User. - Is transaction-agnostic (per
.ai/instructions.md— the caller owns the transaction).
Migrate the existing callers:
JodGig::Users::SyncService(talent batch) — replace its inline single-record upsert in Phase A with a call.JodGig::Users::JitMigrationService(talent JIT) — replace its inline upsert with a call.JodGig::Users::EmployersSyncService(employer batch) — already calls into it via the skeleton from 3.1; wireupsert_identities_usersto call the shared service per row.
This is a refactor — there must be no behaviour change.
Acceptance
- Shared service exists at the path above.
- Three existing callers all route through it.
- All existing talent-sync and JIT tests pass without change.
Depends on
- 4.2 (the employer-specific mapping must exist for the shared service to dispatch on
kind)