timetracker/.tasks/done/TT-004-repository-implementations.md
2026-08-03 21:51:48 +02:00

3.2 KiB

TT-004 — Repository Implementations

Type: Story
Priority: High
Labels: data-layer, repositories
Depends on: TT-002, TT-003
Blocks: TT-005


Summary

Implement the concrete repository classes for each feature. Repositories act as the single access point for the data layer; they delegate to Drift DAOs and map DB rows to domain models.

Repositories to implement

ProjectsRepository

File: lib/features/projects/data/projects_repository.dart

abstract class ProjectsRepository {
  Stream<List<Project>> watchAll();
  Stream<List<Project>> watchActive();       // archivedAt IS NULL
  Future<Project> getById(int id);
  Future<int> create({required String name, required int colorValue, String? description});
  Future<void> update(Project project);
  Future<void> archive(int id);              // sets archivedAt = now
  Future<void> delete(int id);
}

TimeEntriesRepository

File: lib/features/entries/data/entries_repository.dart

abstract class TimeEntriesRepository {
  Stream<List<TimeEntry>> watchAll();
  Stream<List<TimeEntry>> watchByProject(int projectId);
  Stream<List<TimeEntry>> watchByDateRange(DateTime from, DateTime to);
  Future<TimeEntry?> getActiveEntry();       // end_time IS NULL
  Future<int> create(TimeEntry entry);
  Future<void> update(TimeEntry entry);
  Future<void> delete(int id);
}

TagsRepository

File: lib/features/entries/data/tags_repository.dart

abstract class TagsRepository {
  Stream<List<Tag>> watchAll();
  Future<Tag> findOrCreate(String name);
  Future<void> setTagsForEntry(int entryId, List<String> tagNames);
  Future<List<Tag>> getTagsForEntry(int entryId);
}

ReportsRepository

File: lib/features/reports/data/reports_repository.dart

abstract class ReportsRepository {
  Future<Duration> getTotalDuration(DateTime from, DateTime to, {int? projectId});
  Future<Map<int, Duration>> getDurationByProject(DateTime from, DateTime to);
  Future<Map<int, Duration>> getDurationByWeekday(DateTime weekStart);
  Future<Map<int, Duration>> getDurationByDay(DateTime monthStart);
}

Acceptance Criteria

  • Abstract interface + concrete DriftXxxRepository implementation for each
  • Riverpod provider for each concrete implementation
  • All DB access goes through DAOs — no raw SQL in repositories
  • Mapper extension methods convert Drift row → domain model
  • Unit tests for each repository method using in-memory Drift DB

Files to create / modify

  • lib/features/projects/data/projects_repository.dart (replace stub)
  • lib/features/projects/data/drift_projects_repository.dart
  • lib/features/entries/data/entries_repository.dart (replace stub)
  • lib/features/entries/data/drift_entries_repository.dart
  • lib/features/entries/data/tags_repository.dart
  • lib/features/entries/data/drift_tags_repository.dart
  • lib/features/reports/data/reports_repository.dart (replace stub)
  • lib/features/reports/data/drift_reports_repository.dart
  • test/features/*/ — unit tests for each

Notes

  • Keep repositories free of Flutter/UI imports
  • Streams from Drift are reactive — repositories expose them directly to Riverpod providers