Data Layer medium complexity backend
0
Dependencies
3
Dependents
1
Entities
0
Integrations

Description

Data access component for persistent in-app notification records related to certificate expiry. Stores notification state, acknowledgement status, and threshold tracking to support the persistent banner and prevent duplicate dispatches.

Feature: Certificate Expiry Notifications

notification-record-repository

Summaries

The Notification Record Repository is the persistence backbone of the entire certificate expiry alerting system. It ensures that coordinators receive timely, accurate alerts without being overwhelmed by duplicate notifications, and that acknowledgement actions are durably recorded for compliance purposes. By tracking which expiry thresholds have already triggered notifications, the system avoids sending redundant alerts that erode coordinator trust and create alert fatigue. The repository also enables clean state management upon certification renewal, automatically clearing outstanding alerts when a mentor's certification is updated.

This reliability and auditability directly supports organizational accountability in managing workforce compliance.

Medium complexity, backend-only, with no upstream repository dependencies—making it a high-priority early deliverable since it is depended upon by coordinator-acknowledgement-service and course-enrollment-prompt-service. The data model must support several distinct notification states (active, acknowledged, renewal-cleared, enrollment-triggered), and the threshold deduplication logic (getNotificationByThreshold) is the most nuanced piece requiring careful test coverage. Testing scope includes: create and retrieve active notifications, acknowledge flow, renewal-triggered clearance, threshold deduplication, and the markEnrollmentTriggered idempotency check. Confirm with stakeholders whether historical (cleared) notification records must be retained for audit purposes or can be hard-deleted, as this affects schema design and storage planning.

The repository manages a notifications table with columns for mentorId, coordinatorId, threshold (enum: 60d/30d/7d/expired), expiryDate, acknowledgedAt, acknowledgedBy, enrollmentTriggered, and clearedAt. createNotificationRecord inserts a new row only after getNotificationByThreshold confirms no record exists for that mentor+threshold combination—threshold deduplication is the core invariant. getActiveNotificationsForMentor and getActiveNotificationsForCoordinator filter on acknowledgedAt IS NULL AND clearedAt IS NULL. acknowledgeNotification sets acknowledgedAt and acknowledgedBy atomically.

clearNotificationsOnRenewal bulk-updates clearedAt for all active notifications for a mentorId, triggered by the renewal flow. markEnrollmentTriggered flips the enrollmentTriggered boolean. All mutations should use row-level locking or optimistic concurrency to prevent race conditions in the daily batch run. Indexes required on (mentorId, threshold), (coordinatorId, acknowledgedAt), and (mentorId, clearedAt).

Responsibilities

  • Create and store in-app notification records for expiry events
  • Retrieve active (unacknowledged) notifications for a mentor or coordinator
  • Update notification status upon acknowledgement or renewal
  • Track which thresholds have already triggered notifications

Interfaces

createNotificationRecord(mentorId: String, coordinatorId: String, threshold: NotificationThreshold, expiryDate: DateTime)
getActiveNotificationsForMentor(mentorId: String) -> List<ExpiryNotification>
getActiveNotificationsForCoordinator(coordinatorId: String) -> List<ExpiryNotification>
acknowledgeNotification(notificationId: String, coordinatorId: String)
clearNotificationsOnRenewal(mentorId: String)
getNotificationByThreshold(mentorId: String, threshold: NotificationThreshold) -> ExpiryNotification?
markEnrollmentTriggered(notificationId: String)

Related Data Entities (1)

Data entities managed by this component

API Contract

View full contract →
REST /api/v1/expiry-notifications 5 endpoints
GET /api/v1/expiry-notifications
GET /api/v1/expiry-notifications/:notification_id
POST /api/v1/expiry-notifications
PUT /api/v1/expiry-notifications/:notification_id
DELETE /api/v1/expiry-notifications/:notification_id