Repository Data Cleaning
Raw Model to Domain Model
Repositories are responsible for transforming raw GQL models into clean domain models using Convertor-based model convertors.
Repository Data Cleaning
Section titled “Repository Data Cleaning”Overview
Section titled “Overview”Data cleaning is the process of transforming GQL raw models (which may contain nulls, inconsistent types, and GQL-specific structures) into clean domain models ready for UI consumption. This is done in the repository layer using model convertors.
The Model Convertor Pattern
Section titled “The Model Convertor Pattern”Define the Model Convertor
Section titled “Define the Model Convertor”@riverpodConvertor<AttendeeModel, GQueryAttendeeData_attendee_attendeeItems>attendeeModelConvertor(Ref ref) { return Convertor((raw) { return AttendeeModel( id: raw.id ?? '', // Null -> empty string fullName: raw.fullName ?? 'Unknown', // Null -> default value email: raw.email ?? '', jobTitle: raw.jobTitle ?? '', avatarUrl: raw.avatar?.mediaUrl, // Nested null-safe access createdAt: raw.createdAt ?? DateTime.now(), // Null -> current time tags: raw.tags?.nonNulls // Filter nulls from lists .map((t) => t.name ?? '') .where((name) => name.isNotEmpty) .toList() ?? [], ); });}Apply in Repository
Section titled “Apply in Repository”For single items, use .thenMap():
Stream<AttendeeModel> queryItem({AttendeeFilterCriteria? filter}) { final params = SingleRequestParams<GattendeeFilters>( filter: filterConvertor.mightExecute(filter), );
return itemDatasource .map((item) { if (item == null) throw AttendeeNotFoundException(); return item; }) .thenMap(modelConvertor) .execute(params);}For lists, use .thenEach():
Stream<List<AttendeeModel>> queryList({ int? skip, int? take, AttendeeFilterCriteria? filter, AttendeeSortCriteria? sort,}) { final params = ListRequestParams<GattendeeFilters, GattendeeOrder>( skip: skip, take: take, filter: filterConvertor.mightExecute(filter), sort: sortConvertor.mightExecute(sort), );
return listDatasource.thenEach(modelConvertor).execute(params);}Data Cleaning Responsibilities
Section titled “Data Cleaning Responsibilities”The model convertor handles:
| Concern | Example |
|---|---|
| Null handling | raw.name ?? 'Unknown' |
| Default values | raw.count ?? 0 |
| Type conversion | DateTime.parse(raw.dateString) |
| Nested flattening | raw.organization?.name ?? '' |
| List null filtering | raw.items?.nonNulls.toList() ?? [] |
| Enum mapping | StatusEnum.fromString(raw.status) |
| Field renaming | fullName: raw.full_name |
Key Rules
Section titled “Key Rules”- Never expose raw GQL types above the repository - Notifiers and UI only see domain models
- Domain models should be “ready to display” - No further cleaning in the UI layer
- Model convertors are testable - Test each conversion as a pure function
- Use
.mightExecute()for nullable conversions - Handles null input gracefully