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

Description

Orchestrates the awarding of badges by writing earned records to Supabase with earned_at timestamps and triggering downstream notifications. Ensures idempotency so the same badge is never awarded twice to the same peer mentor.

Feature: Achievement Badges & Status Recognition

badge-award-service

Summaries

The Badge Award Service is the transactional backbone of the recognition system, ensuring that every earned badge is recorded accurately, permanently, and exactly once. Its idempotency guarantee protects the integrity of the recognition program — peer mentors can trust that their achievements are faithfully captured and will never be duplicated or lost due to system retries or edge cases. By also triggering downstream notifications at the moment of award, this service ensures that the recognition reaches the user promptly, maximizing its motivational impact and reinforcing trust in the platform's reliability.

Medium complexity backend service. The primary delivery risk is ensuring idempotency is correctly implemented before go-live — a duplicate badge award would undermine user trust and require manual remediation. Depends only on badge-repository, making it one of the more self-contained backend components. Testing must cover: first-time award (success path), duplicate award attempt (idempotent no-op), notification trigger success, and notification trigger failure (should not roll back the award).

The revokeIfCriteriaNoLongerMet() method adds scope — clarify with stakeholders whether badge revocation is in scope for the initial release, as it has UX implications that require product decisions.

awardBadge() should open a database transaction: check hasEarned() first, and if false, insert the EarnedBadge record with earned_at = now(), then commit. hasEarned() queries the earned_badges table by (badge_definition_id, peer_mentor_id) — add a unique constraint on this pair in the schema to enforce idempotency at the database level as a safety net. After commit, fire notification triggers asynchronously (do not block the return). getEarnedBadges() should be paginated or filtered by organisation to avoid unbounded result sets.

revokeIfCriteriaNoLongerMet() deletes the earned record — ensure this is only callable by the evaluation service with appropriate RLS policies. All methods should be callable from Supabase Edge Functions via the service role key.

Responsibilities

  • Write earned badge record with timestamp to database
  • Ensure idempotent award (no duplicate badges)
  • Trigger in-app and push notification on badge earn
  • Return awarded badge details to calling service

Interfaces

awardBadge(String badgeDefinitionId, String peerMentorId) -> EarnedBadge
hasEarned(String badgeDefinitionId, String peerMentorId) -> bool
getEarnedBadges(String peerMentorId) -> List<EarnedBadge>
revokeIfCriteriaNoLongerMet(String badgeDefinitionId, String peerMentorId)

Relationships

Dependencies (1)

Components this component depends on

Dependents (1)

Components that depend on this component

Related Data Entities (3)

Data entities managed by this component

API Contract

View full contract →
REST /api/v1/earned-badges 5 endpoints
GET /api/v1/earned-badges
GET /api/v1/earned-badges/:id
POST /api/v1/earned-badges
DELETE /api/v1/earned-badges/:id
GET /api/v1/earned-badges/check