Repositories API Reference
Complete reference for all Repository interfaces and implementations in Archery Apprentice.
Overview
Repositories provide an abstraction layer over data sources (Room database, Firebase, etc.). They:
- Abstract data source implementation details
- Provide reactive data streams (Flow)
- Handle data mapping between layers
- Implement business data operations
- Support offline-first architecture
Total Repositories: 17
Repository Pattern
Interface Pattern
interface Repository {
suspend fun create(entity: Entity): Result<Id>
suspend fun update(entity: Entity): Result<Unit>
suspend fun delete(id: Id): Result<Unit>
suspend fun getById(id: Id): Result<Entity>
fun observeAll(): Flow<List<Entity>>
}Implementation Pattern
class RepositoryImpl(
private val dao: Dao
) : Repository {
override suspend fun create(entity: Entity): Result<Id> =
try {
val id = dao.insert(entity)
Result.success(id)
} catch (e: Exception) {
Result.failure(e)
}
override fun observeAll(): Flow<List<Entity>> =
dao.observeAll()
}Core Repositories
RoundRepository
Purpose: Round lifecycle and scoring data management
Status: 🚧 Needs comprehensive documentation
File: data/repository/impl/RoundRepository.kt (1,443 lines)
Documentation: Full API Reference →
Key Operations:
- Round CRUD operations
- End score management
- Arrow score tracking
- Round completion
- Historical data queries
- Multi-participant support
Critical Issues:
- ⚠️ God class: 1,443 lines
- ⚠️ N+1 query pattern: Performance issue
- Recommendation: Split into focused repositories
Used extensively in:
Equipment Repositories
BowSetupRepository
Purpose: Bow configuration management
Status: 📝 Needs documentation
File: data/repository/impl/BowSetupRepository.kt
Documentation: Full API Reference →
Key Operations:
- Bow setup CRUD
- Active bow management
- Setup history
- Equipment correlation
Used in: Equipment Flow
ArrowSetupRepository
Purpose: Arrow configuration management
Status: 📝 Needs documentation
File: data/repository/impl/ArrowSetupRepository.kt
SightConfigurationRepository
Purpose: Sight settings management
Status: 📝 Needs documentation
File: data/repository/impl/SightConfigurationRepository.kt
RestConfigurationRepository
Purpose: Arrow rest configuration
Status: 📝 Needs documentation
File: data/repository/impl/RestConfigurationRepository.kt
StabilizerConfigurationRepository
Purpose: Stabilizer setup management
Status: 📝 Needs documentation
File: data/repository/impl/StabilizerConfigurationRepository.kt
PlungerConfigurationRepository
Purpose: Plunger/button configuration
Status: 📝 Needs documentation
File: data/repository/impl/PlungerConfigurationRepository.kt
TabConfigurationRepository
Purpose: Finger tab settings
Status: 📝 Needs documentation
File: data/repository/impl/TabConfigurationRepository.kt
ReleaseAidConfigurationRepository
Purpose: Release aid configuration
Status: 📝 Needs documentation
File: data/repository/impl/ReleaseAidConfigurationRepository.kt
ClkrConfigurationRepository
Purpose: Clicker settings
Status: 📝 Needs documentation
File: data/repository/impl/ClkrConfigurationRepository.kt
StringConfigurationRepository
Purpose: Bowstring configuration
Status: 📝 Needs documentation
File: data/repository/impl/StringConfigurationRepository.kt
LimbsConfigurationRepository
Purpose: Bow limb settings
Status: 📝 Needs documentation
File: data/repository/impl/LimbsConfigurationRepository.kt
RiserConfigurationRepository
Purpose: Bow riser configuration
Status: 📝 Needs documentation
File: data/repository/impl/RiserConfigurationRepository.kt
Analytics Repositories
StatisticsRepository
Purpose: Performance statistics and analytics
Status: 📝 Needs documentation
File: data/repository/impl/StatisticsRepository.kt
Key Operations:
- Score aggregation
- Performance trends
- Equipment correlation
- Session analytics
Tournament Repositories
TournamentRepository (Interface)
Purpose: Tournament data operations abstraction
Status: 🚧 Needs comprehensive documentation
File: data/repository/TournamentRepository.kt (395 lines, 51 methods)
Critical Issues:
- ⚠️ God interface: 51 methods (3.4x too many)
- Recommendation: Split into 4 focused repositories
OfflineTournamentRepository
Purpose: Local tournament storage (Room)
Status: 📝 Needs documentation
File: data/repository/impl/OfflineTournamentRepository.kt (908 lines)
FirebaseTournamentRepository
Purpose: Cloud tournament sync (Firebase Firestore)
Status: 📝 Needs documentation
File: data/repository/impl/FirebaseTournamentRepository.kt (1,707 lines)
Critical Issues:
- ⚠️ God class: 1,707 lines (4.3x too large)
- Recommendation: Extract to focused repositories
HybridTournamentRepository
Purpose: Offline-first tournament with cloud sync
Status: 📝 Needs documentation
File: data/repository/impl/HybridTournamentRepository.kt (1,506 lines)
Key Features:
- Offline-first architecture
- Automatic background sync
- Network connectivity monitoring
- Smart caching with TTL
- Conflict resolution
Used in: Data Sync Flow
Repository God Classes
Critical Issues
Total Repository Code: 5,959 lines across tournament repositories
Problem:
- TournamentRepository interface: 395 lines, 51 methods
- FirebaseTournamentRepository: 1,707 lines
- HybridTournamentRepository: 1,506 lines
- RoundRepository: 1,443 lines
Industry Standard:
- Good repository: 200-400 lines, 10-15 methods
Recommended Refactoring:
Split TournamentRepository into:
- TournamentCrudRepository (~300 lines)
- Basic CRUD operations
- Tournament lifecycle
- TournamentSyncRepository (~400 lines)
- Firebase sync
- Conflict resolution
- TournamentScoreRepository (~500 lines)
- Score submission
- Leaderboard management
- TournamentAnalyticsRepository (~200 lines)
- Statistics
- Performance tracking
See: Checkpoint Findings
Offline-First Pattern
Hybrid Repository Approach
class HybridRepository(
private val localRepo: LocalRepository,
private val remoteRepo: RemoteRepository
) : Repository {
override suspend fun getData(): Result<Data> {
// 1. Return local data immediately
val local = localRepo.getData()
// 2. Sync with remote in background
coroutineScope.launch {
if (isNetworkAvailable) {
val remote = remoteRepo.getData()
if (remote.isSuccess) {
localRepo.update(remote.getOrThrow())
}
}
}
return local
}
}Benefits:
- ✅ Immediate UI response
- ✅ Works offline
- ✅ Automatic sync
- ✅ Network failure resilient
Result Pattern
All repositories use Result
suspend fun operation(): Result<Data> = try {
val data = dao.fetchData()
Result.success(data)
} catch (e: Exception) {
LogConfig.e("Repository", "Operation failed", e)
Result.failure(e)
}Usage:
val result = repository.operation()
result.fold(
onSuccess = { data -> /* Handle success */ },
onFailure = { error -> /* Handle error */ }
)
// Or
val data = result.getOrElse { defaultValue }Flow Pattern
Repositories expose reactive data streams:
fun observeData(): Flow<List<Data>> =
dao.observeData()
.map { entities -> entities.map { it.toDomain() } }
.catch { e ->
LogConfig.e("Repository", "Flow error", e)
emit(emptyList())
}Collection:
@Composable
fun MyScreen(repository: Repository) {
val data by repository.observeData()
.collectAsState(initial = emptyList())
// Use data
}Testing Repositories
Unit Test with Mock DAO
@Test
fun `getData returns success when DAO succeeds`() = runTest {
val mockDao = mockk<Dao>()
every { mockDao.fetchData() } returns testData
val repository = RepositoryImpl(mockDao)
val result = repository.getData()
assertTrue(result.isSuccess)
assertEquals(testData, result.getOrNull())
}Integration Test with Room
@Test
fun `repository persists data correctly`() = runTest {
val database = Room.inMemoryDatabaseBuilder(
context,
AppDatabase::class.java
).build()
val repository = RepositoryImpl(database.dao())
repository.create(testEntity)
val result = repository.getById(testEntity.id)
assertEquals(testEntity, result.getOrNull())
}Performance Considerations
N+1 Query Problem
Problem: RoundRepository.kt:98-102 exhibits N+1 pattern
// BAD: N+1 queries
fun getRoundWithDetails(roundId: Int): RoundWithDetails {
val round = dao.getRound(roundId) // 1 query
val ends = round.ends.map { end ->
dao.getArrowsForEnd(end.id) // N queries
}
}Solution: Use JOIN query
// GOOD: Single query with JOIN
@Query("""
SELECT es.*, COUNT(ars.id) as arrowCount
FROM end_scores es
LEFT JOIN arrow_scores ars ON es.id = ars.endScoreId
WHERE es.roundId = :roundId
GROUP BY es.id
""")
suspend fun getEndScoresSummaryForRound(roundId: Int): List<EndScoreSummary>Impact:
- 30-end round: 31 queries → 1 query
- Expected improvement: 30-60x query reduction
Related Documentation
Architecture:
Flows:
Testing:
Contributing
Help us document the remaining repositories!
Priority Documentation Needed:
- RoundRepository (most critical)
- BowSetupRepository (equipment)
- Tournament repositories (hybrid architecture)
- Equipment-specific repositories (11 remaining)
Template: API Documentation Template
Status: 3/17 Repositories documented (18%) Last Updated: 2025-11-01 Known Issues: 4 god class repositories identified