Skip to content

Commit d289f4a

Browse files
authored
Merge pull request #193 from YAPP-Github/refactor/#192-daily-emotion-flow
[Refactor/#192] DailyEmotion 데이터 흐름 개선
2 parents 64eb15e + ae20777 commit d289f4a

File tree

19 files changed

+175
-102
lines changed

19 files changed

+175
-102
lines changed

app/src/main/java/com/threegap/bitnagil/di/data/DataSourceModule.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import com.threegap.bitnagil.data.auth.datasource.AuthLocalDataSource
88
import com.threegap.bitnagil.data.auth.datasource.AuthRemoteDataSource
99
import com.threegap.bitnagil.data.auth.datasourceimpl.AuthLocalDataSourceImpl
1010
import com.threegap.bitnagil.data.auth.datasourceimpl.AuthRemoteDataSourceImpl
11-
import com.threegap.bitnagil.data.emotion.datasource.EmotionDataSource
12-
import com.threegap.bitnagil.data.emotion.datasourceImpl.EmotionDataSourceImpl
11+
import com.threegap.bitnagil.data.emotion.datasource.EmotionLocalDataSource
12+
import com.threegap.bitnagil.data.emotion.datasource.EmotionRemoteDataSource
13+
import com.threegap.bitnagil.data.emotion.datasourceImpl.EmotionLocalDataSourceImpl
14+
import com.threegap.bitnagil.data.emotion.datasourceImpl.EmotionRemoteDataSourceImpl
1315
import com.threegap.bitnagil.data.file.datasource.FileDataSource
1416
import com.threegap.bitnagil.data.file.datasourceImpl.FileDataSourceImpl
1517
import com.threegap.bitnagil.data.onboarding.datasource.OnBoardingDataSource
@@ -54,7 +56,11 @@ abstract class DataSourceModule {
5456

5557
@Binds
5658
@Singleton
57-
abstract fun bindEmotionDataSource(emotionDataSourceImpl: EmotionDataSourceImpl): EmotionDataSource
59+
abstract fun bindEmotionLocalDataSource(emotionLocalDataSourceImpl: EmotionLocalDataSourceImpl): EmotionLocalDataSource
60+
61+
@Binds
62+
@Singleton
63+
abstract fun bindEmotionRemoteDataSource(emotionRemoteDataSourceImpl: EmotionRemoteDataSourceImpl): EmotionRemoteDataSource
5864

5965
@Binds
6066
@Singleton
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.threegap.bitnagil.data.emotion.datasource
2+
3+
import com.threegap.bitnagil.domain.emotion.model.DailyEmotion
4+
import kotlinx.coroutines.flow.StateFlow
5+
6+
interface EmotionLocalDataSource {
7+
val dailyEmotion: StateFlow<DailyEmotion?>
8+
fun saveDailyEmotion(dailyEmotion: DailyEmotion)
9+
fun clearCache()
10+
}

data/src/main/java/com/threegap/bitnagil/data/emotion/datasource/EmotionDataSource.kt renamed to data/src/main/java/com/threegap/bitnagil/data/emotion/datasource/EmotionRemoteDataSource.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.threegap.bitnagil.data.emotion.model.dto.EmotionDto
44
import com.threegap.bitnagil.data.emotion.model.response.DailyEmotionResponse
55
import com.threegap.bitnagil.data.emotion.model.response.RegisterEmotionResponse
66

7-
interface EmotionDataSource {
7+
interface EmotionRemoteDataSource {
88
suspend fun getEmotions(): Result<List<EmotionDto>>
99
suspend fun registerEmotion(emotion: String): Result<RegisterEmotionResponse>
1010
suspend fun fetchDailyEmotion(currentDate: String): Result<DailyEmotionResponse>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.threegap.bitnagil.data.emotion.datasourceImpl
2+
3+
import com.threegap.bitnagil.data.emotion.datasource.EmotionLocalDataSource
4+
import com.threegap.bitnagil.domain.emotion.model.DailyEmotion
5+
import kotlinx.coroutines.flow.MutableStateFlow
6+
import kotlinx.coroutines.flow.StateFlow
7+
import kotlinx.coroutines.flow.asStateFlow
8+
import kotlinx.coroutines.flow.update
9+
import javax.inject.Inject
10+
11+
class EmotionLocalDataSourceImpl @Inject constructor() : EmotionLocalDataSource {
12+
private val _dailyEmotion = MutableStateFlow<DailyEmotion?>(null)
13+
override val dailyEmotion: StateFlow<DailyEmotion?> = _dailyEmotion.asStateFlow()
14+
15+
override fun saveDailyEmotion(dailyEmotion: DailyEmotion) {
16+
_dailyEmotion.update { dailyEmotion }
17+
}
18+
19+
override fun clearCache() {
20+
_dailyEmotion.update { null }
21+
}
22+
}

data/src/main/java/com/threegap/bitnagil/data/emotion/datasourceImpl/EmotionDataSourceImpl.kt renamed to data/src/main/java/com/threegap/bitnagil/data/emotion/datasourceImpl/EmotionRemoteDataSourceImpl.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package com.threegap.bitnagil.data.emotion.datasourceImpl
22

33
import com.threegap.bitnagil.data.common.safeApiCall
4-
import com.threegap.bitnagil.data.emotion.datasource.EmotionDataSource
4+
import com.threegap.bitnagil.data.emotion.datasource.EmotionRemoteDataSource
55
import com.threegap.bitnagil.data.emotion.model.dto.EmotionDto
66
import com.threegap.bitnagil.data.emotion.model.request.RegisterEmotionRequest
77
import com.threegap.bitnagil.data.emotion.model.response.DailyEmotionResponse
88
import com.threegap.bitnagil.data.emotion.model.response.RegisterEmotionResponse
99
import com.threegap.bitnagil.data.emotion.service.EmotionService
1010
import javax.inject.Inject
1111

12-
class EmotionDataSourceImpl @Inject constructor(
12+
class EmotionRemoteDataSourceImpl @Inject constructor(
1313
private val emotionService: EmotionService,
14-
) : EmotionDataSource {
14+
) : EmotionRemoteDataSource {
1515
override suspend fun getEmotions(): Result<List<EmotionDto>> {
1616
return safeApiCall {
1717
emotionService.getEmotions()

data/src/main/java/com/threegap/bitnagil/data/emotion/model/response/DailyEmotionResponse.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.threegap.bitnagil.domain.emotion.model.DailyEmotion
44
import com.threegap.bitnagil.domain.emotion.model.EmotionMarbleType
55
import kotlinx.serialization.SerialName
66
import kotlinx.serialization.Serializable
7+
import java.time.LocalDate
78

89
@Serializable
910
data class DailyEmotionResponse(
@@ -17,10 +18,11 @@ data class DailyEmotionResponse(
1718
val emotionMarbleHomeMessage: String?,
1819
)
1920

20-
fun DailyEmotionResponse.toDomain(): DailyEmotion =
21+
fun DailyEmotionResponse.toDomain(fetchedDate: LocalDate): DailyEmotion =
2122
DailyEmotion(
2223
type = emotionMarbleType,
2324
name = emotionMarbleName,
2425
imageUrl = imageUrl,
2526
homeMessage = emotionMarbleHomeMessage,
27+
fetchedDate = fetchedDate,
2628
)
Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,84 @@
11
package com.threegap.bitnagil.data.emotion.repositoryImpl
22

3-
import com.threegap.bitnagil.data.emotion.datasource.EmotionDataSource
3+
import com.threegap.bitnagil.data.emotion.datasource.EmotionLocalDataSource
4+
import com.threegap.bitnagil.data.emotion.datasource.EmotionRemoteDataSource
45
import com.threegap.bitnagil.data.emotion.model.response.toDomain
56
import com.threegap.bitnagil.domain.emotion.model.DailyEmotion
67
import com.threegap.bitnagil.domain.emotion.model.Emotion
7-
import com.threegap.bitnagil.domain.emotion.model.EmotionChangeEvent
88
import com.threegap.bitnagil.domain.emotion.model.EmotionRecommendRoutine
99
import com.threegap.bitnagil.domain.emotion.repository.EmotionRepository
1010
import kotlinx.coroutines.flow.Flow
11-
import kotlinx.coroutines.flow.MutableSharedFlow
12-
import kotlinx.coroutines.flow.asSharedFlow
11+
import kotlinx.coroutines.flow.emitAll
12+
import kotlinx.coroutines.flow.filterNotNull
13+
import kotlinx.coroutines.flow.flow
14+
import kotlinx.coroutines.flow.map
15+
import kotlinx.coroutines.sync.Mutex
16+
import kotlinx.coroutines.sync.withLock
17+
import java.time.LocalDate
1318
import javax.inject.Inject
1419

1520
class EmotionRepositoryImpl @Inject constructor(
16-
private val emotionDataSource: EmotionDataSource,
21+
private val emotionRemoteDataSource: EmotionRemoteDataSource,
22+
private val emotionLocalDataSource: EmotionLocalDataSource,
1723
) : EmotionRepository {
24+
25+
private val fetchMutex = Mutex()
26+
1827
override suspend fun getEmotions(): Result<List<Emotion>> {
19-
return emotionDataSource.getEmotions().map { response ->
28+
return emotionRemoteDataSource.getEmotions().map { response ->
2029
response.map { it.toDomain() }
2130
}
2231
}
2332

2433
override suspend fun registerEmotion(emotionMarbleType: String): Result<List<EmotionRecommendRoutine>> {
25-
return emotionDataSource.registerEmotion(emotionMarbleType).map {
26-
it.recommendedRoutines.map {
27-
emotionRecommendedRoutineDto ->
34+
return emotionRemoteDataSource.registerEmotion(emotionMarbleType).map {
35+
it.recommendedRoutines.map { emotionRecommendedRoutineDto ->
2836
emotionRecommendedRoutineDto.toEmotionRecommendRoutine()
2937
}
3038
}.also {
31-
if (it.isSuccess) {
32-
_emotionChangeEventFlow.emit(EmotionChangeEvent.ChangeEmotion(emotionMarbleType))
33-
}
39+
if (it.isSuccess) fetchAndSaveDailyEmotion(today = LocalDate.now(), forceRefresh = true)
3440
}
3541
}
3642

37-
override suspend fun fetchDailyEmotion(currentDate: String): Result<DailyEmotion> =
38-
emotionDataSource.fetchDailyEmotion(currentDate).map { it.toDomain() }
43+
override fun observeDailyEmotion(): Flow<Result<DailyEmotion>> = flow {
44+
fetchAndSaveDailyEmotion(LocalDate.now())
45+
.onFailure {
46+
emit(Result.failure(it))
47+
return@flow
48+
}
49+
50+
emitAll(
51+
emotionLocalDataSource.dailyEmotion
52+
.filterNotNull()
53+
.map { Result.success(it) },
54+
)
55+
}
56+
57+
override fun clearCache() {
58+
emotionLocalDataSource.clearCache()
59+
}
60+
61+
private suspend fun fetchAndSaveDailyEmotion(
62+
today: LocalDate,
63+
forceRefresh: Boolean = false,
64+
): Result<DailyEmotion> {
65+
if (!forceRefresh) {
66+
val currentLocalData = emotionLocalDataSource.dailyEmotion.value
67+
if (currentLocalData != null && !currentLocalData.isStale(today)) {
68+
return Result.success(currentLocalData)
69+
}
70+
}
3971

40-
private val _emotionChangeEventFlow = MutableSharedFlow<EmotionChangeEvent>()
41-
override suspend fun getEmotionChangeEventFlow(): Flow<EmotionChangeEvent> = _emotionChangeEventFlow.asSharedFlow()
72+
return fetchMutex.withLock {
73+
if (!forceRefresh) {
74+
val doubleCheckData = emotionLocalDataSource.dailyEmotion.value
75+
if (doubleCheckData != null && !doubleCheckData.isStale(today)) {
76+
return@withLock Result.success(doubleCheckData)
77+
}
78+
}
79+
emotionRemoteDataSource.fetchDailyEmotion(today.toString())
80+
.onSuccess { emotionLocalDataSource.saveDailyEmotion(it.toDomain(today)) }
81+
.map { it.toDomain(today) }
82+
}
83+
}
4284
}

data/src/main/java/com/threegap/bitnagil/data/user/datasource/UserLocalDataSource.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import kotlinx.coroutines.flow.StateFlow
55

66
interface UserLocalDataSource {
77
val userProfile: StateFlow<UserProfile?>
8-
suspend fun saveUserProfile(userProfile: UserProfile)
8+
fun saveUserProfile(userProfile: UserProfile)
99
fun clearCache()
1010
}

data/src/main/java/com/threegap/bitnagil/data/user/datasourceImpl/UserLocalDataSourceImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class UserLocalDataSourceImpl @Inject constructor() : UserLocalDataSource {
1414
private val _userProfile = MutableStateFlow<UserProfile?>(null)
1515
override val userProfile: StateFlow<UserProfile?> = _userProfile.asStateFlow()
1616

17-
override suspend fun saveUserProfile(userProfile: UserProfile) {
17+
override fun saveUserProfile(userProfile: UserProfile) {
1818
_userProfile.update { userProfile }
1919
}
2020

data/src/test/java/com/threegap/bitnagil/data/user/repositoryImpl/UserRepositoryImplTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class UserRepositoryImplTest {
102102
private val _userProfile = MutableStateFlow<UserProfile?>(null)
103103
override val userProfile: StateFlow<UserProfile?> = _userProfile.asStateFlow()
104104

105-
override suspend fun saveUserProfile(userProfile: UserProfile) {
105+
override fun saveUserProfile(userProfile: UserProfile) {
106106
_userProfile.update { userProfile }
107107
}
108108

0 commit comments

Comments
 (0)