При использовании в первую очередь оффлайн + дизайн, управляемый доменом + чистая архитектура, где следует реализовать отображение logi c от DTO до Room Entity? - PullRequest
1 голос
/ 07 августа 2020

Сначала использовать офлайн или тот, который вы видите в официальном документе и чистую архитектуру с отображением DTO (объект передачи данных, используемый для получения данных REST) ​​в модель базы данных Entity?

С помощью При автономном подходе «первый / последний» вам необходимо иметь 3 разных типа модели для одних и тех же данных и иметь 2 преобразования, которые:

  DTO(Data from network) -> Entity(Database/Room @Entity) -> Item(For presentation or UI)

Второе преобразование - это то, что каждый пример делает на уровне использования или домена. Вопрос здесь в том, где преобразовать из сетевой модели в модель базы данных.

Один дизайн, который я видел в приложении Buffer в github, выглядит примерно так: он немного упрощен,

interface PostDataSource {
    suspend fun getPostEntities(): List<PostEntity>

}

interface LocalPostDataSource : PostDataSource {

    suspend fun savePosts(posts: List<PostEntity>)

    suspend fun deletePosts()
}

interface RemotePostDataSource : PostDataSource

все дело в как удаленный, так и локальный источник данных возвращают объекты базы данных, где отображение logi c происходит внутри удаленного реализации источника данных.

class RemotePostDataSourceImpl(
    private val postApi: PostApi,
    private val mapper: DTOtoEntityMapper
) : RemotePostDataSource {
    override suspend fun getPostEntities(): List<PostEntity> {
        return mapper.map(postApi.getPosts())
    }
}

А в другом я обычно вижу

interface PostDataSource

interface LocalPostDataSource : PostDataSource {

    suspend fun getPostEntities(): List<PostEntity>
    suspend fun savePosts(posts: List<PostEntity>)

    suspend fun deletePosts()
}

interface RemotePostDataSource : PostDataSource {
    suspend fun getPostDTOs(): List<PostDTO>
}

удаленный источник данных

class RemotePostDataSourceImpl(
    private val postApi: PostApi
) : RemotePostDataSource {

    override suspend fun getPostDTOs(): List<PostDTO> {
        return postApi.getPosts()
    }
}

И сопоставление logi c находится внутри репозитория

class PostRepositoryImpl(
    private val localPostDataSource: LocalPostDataSource,
    private val remotePostDataSource: RemotePostDataSource,
    private val mapper: DTOtoEntityMapper
) : PostRepository {

    override suspend fun getPostsOfflineFirstFlow(): Flow<List<PostEntity>> =
        flow {
            emit(localPostDataSource.getPostEntities())
        }
            .flatMapConcat { postEntities ->

                if (postEntities.isNullOrEmpty()) {

                    mapper.map(remotePostDataSource.getPostDTOs())?.apply {
                        localPostDataSource.deletePosts()
                        localPostDataSource.savePosts(this)
                    }

                    flowOf(localPostDataSource.getPostEntities())

                } else {
                    flowOf(postEntities)
                }
            }.catch { emit(listOf()) }

}

Не обращайте внимания на остальную часть кода, кроме сопоставления для этого вопроса, он вызывается в репозитории.

И мой второй вопрос заключается в том, что не помещает c бизнес-логику в репозиторий или удаленный источник данных вместо варианта использования / интерактора (класс с целью реализации бизнес-логи c) для автономного первого рассматриваемого анти-шаблона?

Изменить : Без модели для базы данных / сети / пользовательского интерфейса, сопоставления и источников данных Google предложил код для получение данных с удаленного компьютера и сохранение в БД выполняется на уровне репозитория. Было бы лучше переместить logi c для проверки данных retrieving, refreshing и cacheing на уровень домена, если вы считаете, что это анти-шаблон, чтобы иметь logi c в репозитории или источниках данных?

// Informs Dagger that this class should be constructed only once.
@Singleton
class UserRepository @Inject constructor(
   private val webservice: Webservice,
   // Simple in-memory cache. Details omitted for brevity.
   private val executor: Executor,
   private val userDao: UserDao
) {
   fun getUser(userId: String): LiveData<User> {
       refreshUser(userId)
       // Returns a LiveData object directly from the database.
       return userDao.load(userId)
   }

   private fun refreshUser(userId: String) {
       // Runs in a background thread.
       executor.execute {
           // Check if user data was fetched recently.
           val userExists = userDao.hasUser(FRESH_TIMEOUT)
           if (!userExists) {
               // Refreshes the data.
               val response = webservice.getUser(userId).execute()

               // Check for errors here.

               // Updates the database. The LiveData object automatically
               // refreshes, so we don't need to do anything else here.
               userDao.save(response.body()!!)
           }
       }
   }

   companion object {
       val FRESH_TIMEOUT = TimeUnit.DAYS.toMillis(1)
   }
}
...