Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ ID を元にスケジュールを取得するユースケースを追加 #121

Merged
merged 5 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package club.nito.core.data

import club.nito.core.model.place.Place
import club.nito.core.model.place.PlaceId
import club.nito.core.network.place.PlaceRemoteDataSource

public class DefaultPlaceRepository(
private val remoteDataSource: PlaceRemoteDataSource,
) : PlaceRepository {
override suspend fun fetchPlaceList(vararg ids: PlaceId): List<Place> {
return remoteDataSource.fetchPlaceList(ids.toList())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package club.nito.core.data

import club.nito.core.model.place.Place
import club.nito.core.model.place.PlaceId

/**
* 場所に関するリポジトリ
*/
public sealed interface PlaceRepository {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PlaceRepositoryがsealed interfaceとして定義されていますが、これはリポジトリパターンには一般的ではありません。sealed interfaceは、限定された実装セットを持つ場合に使用されることが多いです。リポジトリは様々なデータソースによって実装されることが期待されているため、ここではsealedを削除することを検討してください。

/**
* 場所を取得する
* @param ids 取得する場所のID
*/
public suspend fun fetchPlaceList(vararg ids: PlaceId): List<Place>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package club.nito.core.data.di
import club.nito.core.data.AuthRepository
import club.nito.core.data.DefaultAuthRepository
import club.nito.core.data.DefaultParticipantRepository
import club.nito.core.data.DefaultPlaceRepository
import club.nito.core.data.DefaultUserRepository
import club.nito.core.data.OfflineFirstScheduleRepository
import club.nito.core.data.ParticipantRepository
import club.nito.core.data.PlaceRepository
import club.nito.core.data.ScheduleRepository
import club.nito.core.data.UserRepository
import org.koin.core.module.Module
Expand All @@ -18,4 +20,5 @@ public val dataModule: Module = module {
singleOf(::OfflineFirstScheduleRepository) bind ScheduleRepository::class
singleOf(::DefaultParticipantRepository) bind ParticipantRepository::class
singleOf(::DefaultUserRepository) bind UserRepository::class
singleOf(::DefaultPlaceRepository) bind PlaceRepository::class
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package club.nito.core.domain

import club.nito.core.data.ParticipantRepository
import club.nito.core.data.PlaceRepository
import club.nito.core.data.ScheduleRepository
import club.nito.core.data.UserRepository
import club.nito.core.domain.extension.toUserIdList
import club.nito.core.domain.model.ParticipantSchedule
import club.nito.core.model.FetchSingleContentResult
import club.nito.core.model.UserProfile
import club.nito.core.model.participant.Participant
import club.nito.core.model.place.Place
import club.nito.core.model.schedule.Schedule
import club.nito.core.model.schedule.ScheduleId
import club.nito.core.model.toNitoError
Expand All @@ -22,6 +24,7 @@ public sealed interface FetchParticipantScheduleByIdUseCase {
public class FetchParticipantScheduleByIdExecutor(
private val scheduleRepository: ScheduleRepository,
private val participantRepository: ParticipantRepository,
private val placeRepository: PlaceRepository,
private val userRepository: UserRepository,
) : FetchParticipantScheduleByIdUseCase {
override suspend fun invoke(id: ScheduleId): FetchSingleContentResult<ParticipantSchedule> {
Expand All @@ -33,10 +36,12 @@ public class FetchParticipantScheduleByIdExecutor(

val participants = participantRepository.getParticipants(id)
val profiles = userRepository.getProfiles(userIds = participants.toUserIdList())
val places = placeRepository.fetchPlaceList(schedule.venueId, schedule.meetId)
val participantSchedule = transformToParticipantSchedule(
schedule = schedule,
participants = participants,
userProfiles = profiles,
places = places,
)

return FetchSingleContentResult.Success(participantSchedule)
Expand All @@ -46,6 +51,7 @@ public class FetchParticipantScheduleByIdExecutor(
schedule: Schedule,
participants: List<Participant>,
userProfiles: List<UserProfile>,
places: List<Place>,
): ParticipantSchedule {
val scheduleParticipants = participants.filter { it.scheduleId == schedule.id }
val scheduleParticipantProfiles = userProfiles.filter { profile ->
Expand All @@ -56,8 +62,8 @@ public class FetchParticipantScheduleByIdExecutor(
id = schedule.id,
scheduledAt = schedule.scheduledAt,
metAt = schedule.metAt,
venueId = schedule.venueId,
meetId = schedule.meetId,
venue = places.first { it.id == schedule.venueId },
meet = places.first { it.id == schedule.meetId },
Comment on lines +65 to +66
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コードには、schedule.venueIdschedule.meetId に対応する場所が places リストに必ず含まれているという前提がありますが、これらの ID がリストにない場合に例外が発生する可能性があります。first メソッドは、条件に一致する要素がない場合に例外を投げるため、安全なアクセスを保証するためにエラーハンドリングを追加することをお勧めします。

val venue = places.firstOrNull { it.id == schedule.venueId } ?: throw SomeException("Venue not found")
val meet = places.firstOrNull { it.id == schedule.meetId } ?: throw SomeException("Meet not found")

description = schedule.description,
participants = scheduleParticipantProfiles,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package club.nito.core.domain

import club.nito.core.data.ParticipantRepository
import club.nito.core.data.PlaceRepository
import club.nito.core.data.ScheduleRepository
import club.nito.core.data.UserRepository
import club.nito.core.domain.model.ParticipantSchedule
import club.nito.core.model.FetchMultipleContentResult
import club.nito.core.model.schedule.Schedule
import club.nito.core.model.UserProfile
import club.nito.core.model.participant.Participant
import club.nito.core.model.place.Place
import club.nito.core.model.schedule.Schedule
import club.nito.core.model.toNitoError
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
Expand All @@ -22,6 +24,7 @@ public sealed interface GetParticipantScheduleListUseCase {
public class GetParticipantScheduleListExecutor(
private val scheduleRepository: ScheduleRepository,
private val participantRepository: ParticipantRepository,
private val placeRepository: PlaceRepository,
private val userRepository: UserRepository,
) : GetParticipantScheduleListUseCase {
override fun invoke(): Flow<FetchMultipleContentResult<ParticipantSchedule>> = flow {
Expand All @@ -40,10 +43,14 @@ public class GetParticipantScheduleListExecutor(
val participants = participantRepository.getParticipants(scheduleIds = schedules.map { it.id })
val profiles = userRepository.getProfiles(userIds = participants.distinctBy { it.userId }.map { it.userId })

val placeIds = (schedules.map { it.meetId } + schedules.map { it.venueId }).distinct()
val places = placeRepository.fetchPlaceList(*placeIds.toTypedArray())

val participantScheduleList = transformToParticipantScheduleList(
schedules = schedules,
participants = participants,
userProfiles = profiles,
places = places,
)

emit(FetchMultipleContentResult.Success(participantScheduleList))
Expand All @@ -53,6 +60,7 @@ public class GetParticipantScheduleListExecutor(
schedules: List<Schedule>,
participants: List<Participant>,
userProfiles: List<UserProfile>,
places: List<Place>,
): List<ParticipantSchedule> = schedules.map { schedule ->
val scheduleParticipants = participants.filter { it.scheduleId == schedule.id }
val scheduleParticipantProfiles = userProfiles.filter { profile ->
Expand All @@ -63,8 +71,8 @@ public class GetParticipantScheduleListExecutor(
id = schedule.id,
scheduledAt = schedule.scheduledAt,
metAt = schedule.metAt,
venueId = schedule.venueId,
meetId = schedule.meetId,
venue = places.first { it.id == schedule.venueId },
meet = places.first { it.id == schedule.meetId },
Comment on lines +74 to +75
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

firstメソッドの使用は、placesリストが大きい場合に非効率的になる可能性があります。firstは一致するものを見つけるまでリストを繰り返し処理するため、特にリストが大きい場合にはパフォーマンスに影響を与える可能性があります。associateByなどを使用して、IDをキーとするマップを事前に作成し、そのマップから直接参照することで効率を向上させることができます。

// 事前にIDをキーとするマップを作成
val placesById = places.associateBy { it.id }
// マップから直接参照
val venue = placesById[schedule.venueId]
val meet = placesById[schedule.meetId]

description = schedule.description,
participants = scheduleParticipantProfiles,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package club.nito.core.domain

import club.nito.core.data.ParticipantRepository
import club.nito.core.data.PlaceRepository
import club.nito.core.data.ScheduleRepository
import club.nito.core.data.UserRepository
import club.nito.core.domain.model.ParticipantSchedule
import club.nito.core.model.FetchSingleContentResult
import club.nito.core.model.Order
import club.nito.core.model.schedule.Schedule
import club.nito.core.model.UserProfile
import club.nito.core.model.place.Place
import club.nito.core.model.schedule.Schedule
import club.nito.core.model.toNitoError
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
Expand All @@ -23,6 +25,7 @@ public sealed interface GetRecentScheduleUseCase {
public class GetRecentScheduleExecutor(
private val scheduleRepository: ScheduleRepository,
private val participantRepository: ParticipantRepository,
private val placeRepository: PlaceRepository,
private val userRepository: UserRepository,
) : GetRecentScheduleUseCase {
override fun invoke(): Flow<FetchSingleContentResult<ParticipantSchedule>> = flow {
Expand All @@ -46,10 +49,12 @@ public class GetRecentScheduleExecutor(

val participants = participantRepository.getParticipants(scheduleId = schedule.id)
val userProfiles = userRepository.getProfiles(userIds = participants.map { it.userId })
val places = placeRepository.fetchPlaceList(schedule.venueId, schedule.meetId)

val participantSchedule = transformToParticipantSchedule(
schedule = schedule,
userProfiles = userProfiles,
places = places,
)

emit(FetchSingleContentResult.Success(participantSchedule))
Expand All @@ -58,13 +63,14 @@ public class GetRecentScheduleExecutor(
private fun transformToParticipantSchedule(
schedule: Schedule,
userProfiles: List<UserProfile>,
places: List<Place>,
): ParticipantSchedule {
return ParticipantSchedule(
id = schedule.id,
scheduledAt = schedule.scheduledAt,
metAt = schedule.metAt,
venueId = schedule.venueId,
meetId = schedule.meetId,
venue = places.first { it.id == schedule.venueId },
meet = places.first { it.id == schedule.meetId },
description = schedule.description,
participants = userProfiles,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package club.nito.core.domain.model

import club.nito.core.model.UserProfile
import club.nito.core.model.place.Place
import club.nito.core.model.schedule.ScheduleId
import kotlinx.datetime.Instant

public data class ParticipantSchedule(
val id: ScheduleId,
val scheduledAt: Instant,
val metAt: Instant,
val venueId: String,
val meetId: String,
val venue: Place,
val meet: Place,
val description: String,
val participants: List<UserProfile>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package club.nito.core.model.place

/**
* 場所
* @param id ID
* @param name 名前
* @param description 説明文
* @param mapUrl 地図URL
* @param imageUrl 画像URL
*/
public data class Place(
val id: PlaceId,
val name: String,
val description: String,
val mapUrl: String,
val imageUrl: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package club.nito.core.model.place

/**
* 場所ID
*/
public typealias PlaceId = String
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import club.nito.core.network.auth.AuthRemoteDataSource
import club.nito.core.network.auth.FakeAuthRemoteDataSource
import club.nito.core.network.participation.FakeParticipantRemoteDataSource
import club.nito.core.network.participation.ParticipantRemoteDataSource
import club.nito.core.network.place.FakePlaceRemoteDataSource
import club.nito.core.network.place.PlaceRemoteDataSource
import club.nito.core.network.schedule.FakeScheduleRemoteDataSource
import club.nito.core.network.schedule.ScheduleRemoteDataSource
import club.nito.core.network.user.FakeUserRemoteDataSource
Expand All @@ -23,4 +25,5 @@ public val fakeRemoteDataSourceModule: Module = module {
single<ScheduleRemoteDataSource> { FakeScheduleRemoteDataSource }
single<ParticipantRemoteDataSource> { FakeParticipantRemoteDataSource }
single<UserRemoteDataSource> { FakeUserRemoteDataSource }
single<PlaceRemoteDataSource> { FakePlaceRemoteDataSource }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import club.nito.core.network.auth.AuthRemoteDataSource
import club.nito.core.network.auth.SupabaseAuthRemoteDataSource
import club.nito.core.network.participation.ParticipantRemoteDataSource
import club.nito.core.network.participation.SupabaseParticipantRemoteDataSource
import club.nito.core.network.place.PlaceRemoteDataSource
import club.nito.core.network.place.SupabasePlaceRemoteDataSource
import club.nito.core.network.schedule.ScheduleRemoteDataSource
import club.nito.core.network.schedule.SupabaseScheduleRemoteDataSource
import club.nito.core.network.user.SupabaseUserRemoteDataSource
import club.nito.core.network.user.UserRemoteDataSource
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module

public val remoteDataSourceModule: Module = module {
Expand Down Expand Up @@ -41,4 +45,5 @@ public val remoteDataSourceModule: Module = module {
client = get(),
)
}
singleOf(::SupabasePlaceRemoteDataSource) bind PlaceRemoteDataSource::class
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package club.nito.core.network.place

import club.nito.core.model.place.Place
import club.nito.core.model.place.PlaceId
import club.nito.core.network.place.model.NetworkPlace
import club.nito.core.network.place.model.createFakeNetworkPlace

public data object FakePlaceRemoteDataSource : PlaceRemoteDataSource {
override suspend fun fetchPlaceList(idList: List<PlaceId>): List<Place> = idList.map { id ->
createFakeNetworkPlace(
id = id,
).let(NetworkPlace::toPlace)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package club.nito.core.network.place

import club.nito.core.model.place.Place
import club.nito.core.model.place.PlaceId

/**
* 場所リモートデータソース
*/
public sealed interface PlaceRemoteDataSource {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PlaceRemoteDataSourceがsealed interfaceとして定義されていますが、これは一般的なデータソースインターフェースの使用法とは異なります。sealed interfaceは、限定された実装セットを持つ場合に使用されることが多いです。データソースのように、潜在的に多くの実装が考えられる場合には、sealedを使用することは推奨されません。この設計意図を再検討することをお勧めします。

/**
* リモートから場所を取得する
* @param idList 取得する場所のID
*/
public suspend fun fetchPlaceList(idList: List<PlaceId>): List<Place>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package club.nito.core.network.place

import club.nito.core.model.place.Place
import club.nito.core.model.place.PlaceId
import club.nito.core.network.NetworkService
import club.nito.core.network.place.model.NetworkPlace
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.postgrest.postgrest

private enum class Column(val columnName: String) {
ID(columnName = "id"),
DELETED_AT(columnName = "deleted_at"),
}

public class SupabasePlaceRemoteDataSource(
private val networkService: NetworkService,
private val client: SupabaseClient,
) : PlaceRemoteDataSource {
private val postgrest = client.postgrest["places"]

override suspend fun fetchPlaceList(idList: List<PlaceId>): List<Place> = networkService {
postgrest
.select {
filter {
isIn(Column.ID.columnName, idList)
exact(Column.DELETED_AT.columnName, null)
}
}
.decodeList<NetworkPlace>()
.map(NetworkPlace::toPlace)
}
}
Loading