critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

MentorLocation is a Dart class/record with fields: mentorId (String), coordinates (LatLng or custom Coordinates value object), consentStatus (ConsentStatus enum), specialisations (List<String>), availabilityStatus (AvailabilityStatus enum) — all fields non-nullable unless explicitly justified
FilterCriteria is a Dart class/record with fields: availabilityStatus (AvailabilityStatus? — nullable for 'any'), specialisationTags (List<String> — empty means no filter), boundingBox (BoundingBox? — nullable for no spatial constraint)
BoundingBox is a value object with fields: northEastLat, northEastLng, southWestLat, southWestLng (all double) with a contains(Coordinates) method
All three models implement Equatable (or use Dart records with structural equality) so identical instances compare as equal — verified by unit test
All models are immutable: no setters, all fields final, copyWith methods provided
Models are located in lib/features/geographic_map/domain/models/ and have no Flutter framework imports (pure Dart)
ConsentStatus and AvailabilityStatus enums are defined in the same domain layer with values matching the database schema
Unit tests assert: equality, copyWith preserving unmodified fields, BoundingBox.contains returning true/false for boundary coordinates

Technical Requirements

frameworks
Flutter (Dart only — no Flutter imports in domain layer)
equatable or Dart records
data models
MentorLocation
FilterCriteria
BoundingBox
ConsentStatus
AvailabilityStatus
performance requirements
Model instantiation must be allocation-efficient — avoid unnecessary heap allocation in hot filter paths
BoundingBox.contains must run in O(1)
security requirements
MentorLocation must not expose raw GPS coordinates to serialization layer without consent status check — document this constraint in a code comment

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Prefer Dart 3 records or freezed-generated classes for immutability and copyWith without boilerplate. If the team is not yet using freezed, use equatable with manual copyWith — keep the pattern consistent with existing domain models in the codebase. Place enums (ConsentStatus, AvailabilityStatus) in separate files within the same domain/models/ directory so they can be imported independently. LatLng from google_maps_flutter carries a Flutter dependency — define a lightweight Coordinates(double lat, double lng) value object in the domain layer instead, and convert to LatLng only at the presentation layer.

This keeps the domain layer testable without a Flutter environment.

Testing Requirements

Write unit tests in test/features/geographic_map/domain/models/ using flutter_test. Test cases: (1) MentorLocation equality — two instances with identical field values are equal, (2) MentorLocation inequality — changing any single field breaks equality, (3) FilterCriteria with all nulls/empty lists represents 'no filter' — document this semantic in a test description, (4) BoundingBox.contains returns true for a coordinate on the boundary and inside, false for outside, (5) copyWith preserves all unmodified fields. No mocking required — these are pure value objects.

Component
Mentor Location Service
service high
Epic Risks (2)
medium impact medium prob technical

The dual BLoC state machines (map view state + filter state) may introduce subtle synchronisation bugs where filter changes do not correctly re-trigger viewport queries, causing stale data to appear on the map.

Mitigation & Contingency

Mitigation: Define all BLoC state transitions in a state diagram before implementation. Use flutter_bloc's BlocObserver in development mode to log every state transition. Write explicit unit tests for filter-change → re-query transitions.

Contingency: If state synchronisation bugs appear in integration testing, refactor to a single unified BLoC that owns both map viewport state and filter state, eliminating cross-BLoC dependencies.

low impact medium prob scope

Cached mentor location data may become stale (mentors move, pause, or revoke consent) and coordinators in offline mode could be shown incorrect mentor information, leading to wasted outreach.

Mitigation & Contingency

Mitigation: Display a clear timestamp on cached data indicating when it was last synced. Set cache TTL to 24 hours and show an 'offline — data from [date]' banner. Revoked consent removes the mentor from the cache on next successful sync via contact-cache-sync-repository.

Contingency: If cache staleness causes user complaints, reduce TTL to 4 hours and implement background sync on app foreground. Accept that very-recently-revoked mentors may appear briefly in offline mode — document this as a known limitation in the privacy policy.