CODEX // oaoisme wiki

§ unswayed-backend

Profiles (Phase 3)

updated 2026-06-05

Phase 3 builds the two profile surfaces that sit on top of the auth identity (Phase 0) and the reference tables (Phase 2): the applicant profile and the employer profile, plus the public/shared profile viewer and the follow-graph reads those resources expose.

It's the first phase where the product gets a face: a user can fill in who they are, upload a résumé, and be looked at by others.

Why it sits here

  • It needs Phase 1's platform layer: résumé/education/photo media flow through the single MediaStoragePort (Cloudinary, log adapter in dev/test).
  • It needs Phase 2's master data: a profile stores country/state/city/gender/ethnicity as ids; the resources resolve them to {id, name} against the seeded lookup tables.
  • Phase 4 (employer jobs) depends on it: a job posting belongs to an Employer, and the network-jobs feed reads the follow graph these resources expose — so the employer must be a real, profile-complete entity first.

What landed

Five new Prisma models — ApplicantEducation, ApplicantResume, ApplicantFilterPreference, EmployerReview, UserFollower — plus Applicant.ucn/skills and the hidden Google-Calendar token columns on Employer. Two NestJS modules:

  • applicant-profile/ — profile read/update (§29), education CRUD (§30), the résumé builder + Affinda + AI-enhance pipeline (§35/§26/§36), and sticky filter preferences (§38). See Applicant profile.
  • employer-profile/ — the Employer/EmployerReview models, self read/update + change-pic (§40), the guest short profile (§5), and the type-branching viewer (§14). See Employer profile & viewer.

The shared edge (the part both modules lean on)

A few pieces are deliberately shared, single-owner so the two modules can't drift:

  • Resource mappers are pure functions (toApplicantResource, toEmployerResource, toEmployerProfileResource, toApplicantProfileResource, toPopularCompanyResource, …). They map a Prisma row + pre-resolved auxiliary data (resolved {id,name} refs, the follow network, review aggregates) to the exact snake_case wire shape. They do no I/O — the services resolve everything async and hand the mapper a finished view-model.
  • MasterDataLookupService (in master-data/) resolves the five reference ids to {id, name} objects in one parallel batch.
  • FollowsService (in users/) reads the UserFollower graph: top-5 followers/followings (filtered to completed + active accounts), the total counts, and per-entry isFollowed resolved against the viewing user.
  • UCN generation lives in UsersService (generateUcn) and runs at applicant-create — completing a gap deferred from Phase 0.

A note on the contract

Everything here is reconciled to the frozen endpoint_documentation.md (§5/§14/§26/§29/§30/§35/§36/§38/§40). The one judgement call — reference fields serialize as {id, name} objects on the self/viewer resources (and .name strings on the applicant viewer, matching legacy) — is recorded as an internal deviation in API-CONTRACT.md / ADR-0026. The gate: 995 unit tests @ ≥90% coverage + 124 e2e, and the build dashboard reports Phase 3 done with drift 0.

Subpages