01 — Product Overview (Parents Module)
- 1. Purpose
- 2. Business goals
- 3. User goals
- 4. Stakeholders
- 5. Why this exists
- 6. Dependencies
- 7. Success metrics (proposed)
- 8. Edge cases
- 9. Assumptions (module)
- 10. Open questions (module-level; global ledger in 00-shared/12)
- 11. Glossary (this module)
StudyLyon — multi-tenant ERP / School Management API. This package designs the Parents module client (guardian profiles, student–parent linking, multi-child support) against the implemented NestJS backend. All endpoints, DTO fields, schemas, events, and wire contracts are derived directly from
src/modules/parents/**,src/modules/students/**,src/modules/users/**,src/modules/rbac/**,src/infrastructure/**,studylyon-blueprint/04-Modules/Parents.md, andstudylyon-blueprint/03-Database/COLLECTIONS.md. No feature is invented; gaps are flagged(planned)/(forward-looking)/(proposed)and itemized in the Assumptions & Open Questions section.
1. Purpose
The Parents module manages guardian identity (occupation, company, income,
emergency-contact priority, pickup authorization) and the many-to-many graph
between guardians and students (student_parent_links). The blueprint defines the
module as "Guardians and emergency contacts" (04-Modules/Parents.md:3) with four
responsibilities: guardian profile, student–parent linking, relationship type, and
emergency-contact/pickup/financial flags (04-Modules/Parents.md:11-17).
| Responsibility | Source |
|---|---|
| Parent profile CRUD (occupation, company, income, emergency flags) | parent.schema.ts:8-32, parent.controller.ts:29-46 |
One parents doc per users doc (unique tenantId+userId) | parent.schema.ts:36 |
| Link parents ↔ students (many-to-many) with relationship metadata | student-parent-link.schema.ts:16-41, student-parent-link.service.ts:16-41 |
| Relationship type enum (mother, father, guardian, grandparent, relative, foster_parent) | student-parent-link.schema.ts:7-14 |
isPrimaryGuardian — primary-communication flag on the link | student-parent-link.schema.ts:27-29 |
| Financial responsibility / pickup authorization / emergency priority | student-parent-link.schema.ts:30-38, parent.schema.ts:24-29 |
| Domain events → BullMQ (in-app notification, audit) | parent.service.ts:38-45,78-86,92-99, event-queue-map.ts:37-39 |
| Soft delete (never hard delete; links survive parent deletion) | parent.service.ts:89-100, base.repository.ts:68-74 |
Identity split (critical architectural rule):
parentsstores guardian business data only. Name, email, phone, avatar live onusers(COLLECTIONS.md §2.3"Domain collections store domain data";users/schemas/user.schema.ts:16-78). The client must always resolve a parent's display identity through the linkedUser(parent.schema.ts:9-10).
2. Business goals
| Goal | Measure / evidence |
|---|---|
| One guardian profile, many children | Link table is the only M2M (student-parent-link.schema.ts:18-25); no child list stored on parents |
| Never duplicate profile data | parents.userId required ref (parent.schema.ts:9); identity lives in users |
| Primary-guardian semantics per child | isPrimaryGuardian per link (student-parent-link.schema.ts:27) |
| Emergency-contact order per child | emergencyPriority on both profile (default 0, parent.schema.ts:24-25) and link (default 0, student-parent-link.schema.ts:36-37) |
| Pickup authorization per child | pickupAuthorization (profile, default false) + pickupAllowed (link, default true) — note the opposite defaults (parent.schema.ts:27-28, student-parent-link.schema.ts:33-34) |
| Removing a link never deletes a parent linked elsewhere | DELETE /parents only soft-deletes the parent doc (parent.service.ts:89-100); link rows are independent (student-parent-link.schema.ts) |
| Multi-tenant isolation | every query scoped tenantId + isDeleted:false (base.repository.ts:20-30); parent index {tenantId, userId} unique (parent.schema.ts:36) |
3. User goals
- Org admin / admission staff: create a guardian profile from an existing user, link the guardian to one or more students, mark the primary guardian, switch primary when custody changes, unlink a guardian cleanly.
- Parent / guardian: log in and see only their linked children (privacy
boundary — persona: "Access: Only linked children",
USER_PERSONAS.md:48-53), identify their own relationship per child (mother/father/…), see who is the primary guardian, update their own emergency-contact/pickup flags. - Multiple-guardian households: mother + father + grandparent each have their own login; each sees the same children; primary flag is per child.
- Admission staff: during enrollment, attach guardians to the newly created
student record (
POST /students→ then link).
4. Stakeholders
Institution admins, admission/front-desk staff, teachers (view linked guardians),
parents/guardians (self-service, forward-looking), accountants (financial-responsibility
flag feeds billing — fees module), platform support (audit trail via ParentUpdated /
ParentDeleted audit events, event-queue-map.ts:38-39), QA + design + engineering.
5. Why this exists
Guardians are the school's primary external stakeholder: they receive attendance,
results, and fee communications (blueprint: "Notifications — absentee/result alerts to
linked parents", 04-Modules/Parents.md:49). Correctness of the link graph and the
primary-guardian flag determines who gets billed, who gets pickups, and who gets
called in an emergency — three flows where a wrong link is a real-world incident.
6. Dependencies
| Dependency | Role | Source |
|---|---|---|
| Users module | identity profile the parent links to (userId) | parent.schema.ts:9; users.service.ts:49 |
| Students module | students must exist before linking | student-parent-link.service.ts:27 (imports StudentRepository) |
| RBAC | global RbacGuard + parent role (student.read only) | app.module.ts:131, role.schema.ts:50-56 |
| Events / BullMQ | ParentCreated → in-app parent-created; ParentUpdated/ParentDeleted → audit | event-queue-map.ts:37-39 |
| Mongo collections | parents, student_parent_links | COLLECTIONS.md (§ parents / § student_parent_links) |
| BaseRepository / TenantContext | tenant scoping + soft-delete filtering | base.repository.ts:20-36 |
7. Success metrics (proposed)
- Zero cross-tenant parent reads (repository scoping structural,
base.repository.ts:20-30). - Link graph integrity: every link's
studentId/parentIdresolves to a non-deleted doc in the same tenant (today not enforced — see OQ-3). - Parent self-service adoption: % of parents who log in (requires
(forward-looking)my-children endpoints — see OQ-1). - Duplicate-link incidents = 0 (today not prevented — OQ-2).
- Emergency-contact list accuracy: % of children with ≥ 1 primary guardian (not enforced).
8. Edge cases
- Duplicate profile: second
POST /parentswith the sameuserId→ 409DUPLICATE_RESOURCE"Parent profile already exists for this user." (parent.service.ts:30-34, enforced by unique indexparent.schema.ts:36). - Parent not found:
GET/PATCH/DELETE /parents/:idandGET /parents/:id/students→ 404RESOURCE_NOT_FOUND"Parent not found." (parent.service.ts:51,77,91,70). - Link to missing student:
POST /parents/link/:studentIdcallsstudentRepo.findById(studentId)but discards the result — a link to a non-existent student is silently created (OQ-3;student-parent-link.service.ts:27). - Duplicate link: same
(studentId, parentId)twice → two link docs allowed (index non-unique,student-parent-link.schema.ts:46) (OQ-2). - Multiple primary guardians: no server rule prevents two links with
isPrimaryGuardian:truefor one student (OQ-5). - Unlink:
DELETE /parents/link/:linkIdsoft-deletes the link; if it was the only link, no parent deletion happens and no re-promotion of another guardian occurs (OQ-4). - Delete parent with active links: parent soft-deleted, links remain orphaned (pointing at a soft-deleted parent) — no cascade (OQ-3).
- Invalid relationship string:
LinkParentDto.relationshipis@IsString()with no enum validation (link-parent.dto.ts:19-20); schema enum rejects on save → MongooseValidationError→ 500INTERNAL_SERVER_ERRORinstead of 400 (OQ-6). - Invalid ObjectId in
:id:CastError→ 400VALIDATION_ERROR"Invalid resource identifier." (http-exception.filter.ts:48,92). - Parent list ignores
qandsort:find()applies only skip/limit (parent.service.ts:58-61) — search UI must not rely onq(OQ-7).
9. Assumptions (module)
- Mobile client is forward-looking: backend is complete; this package is the UI-side spec (same policy as the auth package — PRD Phase 1 excludes native apps, 00-shared/12 A1).
- RBAC is not wired to these endpoints. Global
RbacGuardruns (app.module.ts:131), butParentControllerdeclares only@UseGuards(JwtAuthGuard)(parent.controller.ts:23) and no@Permissions(...); theparent.*permissions in the blueprint (04-Modules/Parents.md:64-70) do not exist inALL_PERMISSIONS(permissions.constants.ts:1-97). Any authenticated user can create/read/update/delete any parent in their tenant. UI must be built with intended-role gating and harden when RBAC lands (OQ-8). - There is no "my parent profile" resolution. No
GET /parents/meand nouserIdfilter onGET /parents; a logged-in parent cannot resolve their own parent profile or children with today's API. The my-children surface is(forward-looking)and needs either a new endpoint or a client-side mapping (OQ-1). Parent role carries onlystudent.read(role.schema.ts:55). - Parent user accounts are created by admins via
POST /users(no invite/password bootstrap specific to parents; noauth_accountscreation from the users module) (OQ-9).ParentCreatedfires only afterPOST /parents(parent.service.ts:38-45). - No email on parent creation:
ParentCreatedroutes to thein-appqueue (event-queue-map.ts:37);email.worker.tshandles onlyUserRegisteredandPasswordResetRequested(email.worker.ts:25-42). - Blueprint link routes (
POST /students/:id/parents,DELETE /students/:id/parents/:linkId,04-Modules/Parents.md:29-30) differ from the implemented controller routes (/parents/link/...,parent.controller.ts:47-60). Implementation wins; blueprint is historical.
10. Open questions (module-level; global ledger in 00-shared/12)
| # | Item | Impact |
|---|---|---|
| OQ-1 | No endpoint to fetch "my parent profile / my children" for the parent role (no /parents/me, no userId filter). Add? | my-children screen is (forward-looking); privacy boundary cannot be exercised today |
| OQ-2 | Duplicate (studentId, parentId) links not prevented (non-unique index, no check). Add unique index + 409? | QA duplicate-link case, link sheet double-submit |
| OQ-3 | linkStudentParent ignores the findById result; links to non-existent students, and links referencing soft-deleted parents, are stored. Enforce? | QA link-validation case; data integrity |
| OQ-4 | Unlink does not reassign isPrimaryGuardian when the primary is unlinked; no rule enforces exactly one primary per student. | primary-guardian switch UX; "who gets billed/called" |
| OQ-5 | isPrimaryGuardian on two links for one student is allowed. Client must self-enforce (or server adds exclusivity). | QA primary-guardian case |
| OQ-6 | relationship in LinkParentDto is free string (@IsString()), schema enum is the only gate → invalid value yields 500, not 400. Add @IsEnum(RelationshipType)? | client must always send valid enum; error mapping |
| OQ-7 | GET /parents ignores q and sort query params (only page/limit applied, parent.service.ts:58-61). | parents list has no search/sort; UI must not fake it |
| OQ-8 | No parent.* permissions exist in ALL_PERMISSIONS; endpoints JWT-only. When does RBAC land? | role-gating of every screen; who may link/unlink |
| OQ-9 | No parent-invitation flow (user + auth account + welcome email). Backend auth_accounts creation is out of the users module's scope. | how a parent obtains login credentials |
| OQ-10 | GET /parents/:id/students returns raw link docs (studentId ObjectIds, student-parent-link.service.ts:34-36) — no populate or student summary. Client must join. | linked-children cards need N+1 fetches or a (planned) enriched endpoint |
11. Glossary (this module)
| Term | Meaning |
|---|---|
| Parent / Guardian | Business profile (parents) linked to identity (users.userId) |
| Link | student_parent_links doc: one parent–student relationship with metadata |
| RelationshipType | mother | father | guardian | grandparent | relative | foster_parent (student-parent-link.schema.ts:7-14) |
| Primary guardian | isPrimaryGuardian flag per link; intended recipient of primary communications (04-Modules/Parents.md:57) |
| Emergency priority | emergencyContactPriority (profile) / emergencyPriority (link), default 0 |
| Pickup | pickupAuthorization (profile, default false) / pickupAllowed (link, default true) |
| Financial responsibility | financialResponsibility (link, default false) |
| Envelope | {success,message,data,meta?,timestamp,requestId} (response-envelope.interceptor.ts:11-62) |