Skip to content

Commit

Permalink
Refactor app architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
halilozcan committed Jan 28, 2023
1 parent d2be4a3 commit 8ef850c
Show file tree
Hide file tree
Showing 30 changed files with 245 additions and 228 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ android {

dependencies {
implementation(project(":core:common"))
implementation(project(":core:data"))
implementation(project(":core:design"))
implementation(project(":core:domain"))
implementation(project(":core:data"))
implementation(project(":feature:home"))
implementation(project(":feature:detail"))

Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
id 'com.google.dagger.hilt.android' version '2.43' apply false
id 'org.jetbrains.kotlin.jvm' version '1.7.20' apply false
id 'com.vanniktech.dependency.graph.generator' version '0.8.0' apply false
}

ext {
Expand Down
7 changes: 7 additions & 0 deletions core/common/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
}

android {
Expand Down Expand Up @@ -35,6 +37,11 @@ dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'

// Dagger Hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
Expand Down
4 changes: 4 additions & 0 deletions core/data/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ android {
dependencies {

implementation(project(":core:common"))
implementation(project(":core:domain"))

implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
Expand Down Expand Up @@ -62,4 +63,7 @@ dependencies {

// Truth
testImplementation "com.google.truth:truth:$truth_version"

// Turbine
testImplementation "app.cash.turbine:turbine:$turbine_version"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.halilozcan.animearch.core.data

import com.halilozcan.animearch.core.common.NetworkResponseState

fun <I:Any,O:Any> NetworkResponseState<I>.mapResponse(mapper:I.()->O): NetworkResponseState<O> {
return when(this){
is NetworkResponseState.Error -> NetworkResponseState.Error(this.exception)
NetworkResponseState.Loading -> NetworkResponseState.Loading
is NetworkResponseState.Success -> NetworkResponseState.Success(mapper.invoke(this.result))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import retrofit2.http.GET
import retrofit2.http.Path

interface AnimeApi {

@GET("top/characters")
suspend fun getTopCharacters(): TopAnimeCharacterResponse

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.halilozcan.animearch.core.domain.di
package com.halilozcan.animearch.core.data.di

import com.halilozcan.animearch.core.data.dto.single.AnimeCharacter
import com.halilozcan.animearch.core.data.dto.top.Anime
import com.halilozcan.animearch.core.data.mapper.SingleAnimeEntityMapper
import com.halilozcan.animearch.core.data.mapper.TopAnimeEntityMapper
import com.halilozcan.animearch.core.domain.entity.SingleAnimeEntity
import com.halilozcan.animearch.core.domain.entity.TopAnimeEntity
import com.halilozcan.animearch.core.domain.mapper.AnimeBaseMapper
import com.halilozcan.animearch.core.domain.mapper.AnimeListMapper
import com.halilozcan.animearch.core.domain.mapper.SingleAnimeEntityMapper
import com.halilozcan.animearch.core.domain.mapper.TopAnimeEntityMapper
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.halilozcan.animearch.core.data.di

import com.halilozcan.animearch.core.data.repository.AnimeRepository
import com.halilozcan.animearch.core.data.repository.AnimeRepositoryImpl
import com.halilozcan.animearch.core.domain.repository.AnimeRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -11,7 +11,6 @@ import dagger.hilt.android.scopes.ViewModelScoped
@Module
@InstallIn(ViewModelComponent::class)
abstract class AnimeRepositoryModule {

@Binds
@ViewModelScoped
abstract fun bindRepository(animeRepositoryImpl: AnimeRepositoryImpl): AnimeRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import kotlinx.coroutines.Dispatchers
@InstallIn(ViewModelComponent::class)
@Module
object CoroutineDispatchersModule {

@IoDispatcher
@Provides
@ViewModelScoped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ import dagger.hilt.android.scopes.ViewModelScoped
@InstallIn(ViewModelComponent::class)
abstract class SourceModule {


@Binds
@ViewModelScoped
abstract fun bindRemoteDataSource(
remoteDataSourceImpl: RemoteDataSourceImpl
): RemoteDataSource
abstract fun bindRemoteDataSource(remoteDataSourceImpl: RemoteDataSourceImpl): RemoteDataSource
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.halilozcan.animearch.core.domain.mapper
package com.halilozcan.animearch.core.data.mapper

import com.halilozcan.animearch.core.data.dto.single.AnimeCharacter
import com.halilozcan.animearch.core.domain.entity.SingleAnimeEntity
import com.halilozcan.animearch.core.domain.mapper.AnimeBaseMapper
import javax.inject.Inject

class SingleAnimeEntityMapper @Inject constructor() :
AnimeBaseMapper<com.halilozcan.animearch.core.data.dto.single.AnimeCharacter, SingleAnimeEntity> {

override fun map(input: com.halilozcan.animearch.core.data.dto.single.AnimeCharacter): SingleAnimeEntity {
AnimeBaseMapper<AnimeCharacter, SingleAnimeEntity> {
override fun map(input: AnimeCharacter): SingleAnimeEntity {
return SingleAnimeEntity(
id = input.malId!!.toString(),
name = input.name.orEmpty(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.halilozcan.animearch.core.domain.mapper
package com.halilozcan.animearch.core.data.mapper

import com.halilozcan.animearch.core.data.dto.top.Anime
import com.halilozcan.animearch.core.domain.entity.TopAnimeEntity
import com.halilozcan.animearch.core.domain.mapper.AnimeListMapper
import javax.inject.Inject

class TopAnimeEntityMapper @Inject constructor() :
AnimeListMapper<com.halilozcan.animearch.core.data.dto.top.Anime, TopAnimeEntity> {

override fun map(input: List<com.halilozcan.animearch.core.data.dto.top.Anime>): List<TopAnimeEntity> {
class TopAnimeEntityMapper @Inject constructor() : AnimeListMapper<Anime, TopAnimeEntity> {
override fun map(input: List<Anime>): List<TopAnimeEntity> {
return input.map {
TopAnimeEntity(
it.malId?.toString().orEmpty(),
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,36 @@ package com.halilozcan.animearch.core.data.repository

import com.halilozcan.animearch.core.common.NetworkResponseState
import com.halilozcan.animearch.core.data.di.IoDispatcher
import com.halilozcan.animearch.core.data.dto.single.SingleCharacterResponse
import com.halilozcan.animearch.core.data.dto.top.TopAnimeCharacterResponse
import com.halilozcan.animearch.core.data.dto.single.AnimeCharacter
import com.halilozcan.animearch.core.data.dto.top.Anime
import com.halilozcan.animearch.core.data.mapResponse
import com.halilozcan.animearch.core.data.source.RemoteDataSource
import com.halilozcan.animearch.core.domain.entity.SingleAnimeEntity
import com.halilozcan.animearch.core.domain.entity.TopAnimeEntity
import com.halilozcan.animearch.core.domain.mapper.AnimeBaseMapper
import com.halilozcan.animearch.core.domain.mapper.AnimeListMapper
import com.halilozcan.animearch.core.domain.repository.AnimeRepository
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class AnimeRepositoryImpl @Inject constructor(
private val remoteDataSource: RemoteDataSource,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) :
AnimeRepository {
@IoDispatcher private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
private val singleAnimeMapper: AnimeBaseMapper<AnimeCharacter, SingleAnimeEntity>,
private val topAnimeEntityMapper: AnimeListMapper<Anime, TopAnimeEntity>
) : AnimeRepository {

override suspend fun getTopAnimeCharacters(): NetworkResponseState<TopAnimeCharacterResponse> =
withContext(ioDispatcher) {
try {
remoteDataSource.getTopAnimeCharacters()
} catch (e: Exception) {
NetworkResponseState.Error(e)
}
override fun getTopAnimeCharacters(): Flow<NetworkResponseState<List<TopAnimeEntity>>> =
remoteDataSource.getTopAnimeCharacters().map {
it.mapResponse { topAnimeEntityMapper.map(data) }
}

override suspend fun getSingleCharacter(id: String): NetworkResponseState<SingleCharacterResponse> =
withContext(ioDispatcher) {
try {
remoteDataSource.getSingleCharacter(id)
} catch (e: Exception) {
NetworkResponseState.Error(e)
}

override fun getSingleCharacter(id: String): Flow<NetworkResponseState<SingleAnimeEntity>> =
remoteDataSource.getSingleCharacter(id).map {
it.mapResponse { singleAnimeMapper.map(data!!) }
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.halilozcan.animearch.core.data.source

import com.halilozcan.animearch.core.common.NetworkResponseState
import com.halilozcan.animearch.core.data.dto.single.SingleCharacterResponse
import com.halilozcan.animearch.core.data.dto.top.TopAnimeCharacterResponse
import kotlinx.coroutines.flow.Flow

interface RemoteDataSource {

suspend fun getTopAnimeCharacters(): com.halilozcan.animearch.core.common.NetworkResponseState<TopAnimeCharacterResponse>
suspend fun getSingleCharacter(id: String): com.halilozcan.animearch.core.common.NetworkResponseState<SingleCharacterResponse>
fun getTopAnimeCharacters(): Flow<NetworkResponseState<TopAnimeCharacterResponse>>
fun getSingleCharacter(id: String): Flow<NetworkResponseState<SingleCharacterResponse>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,30 @@ import com.halilozcan.animearch.core.common.NetworkResponseState
import com.halilozcan.animearch.core.data.api.AnimeApi
import com.halilozcan.animearch.core.data.dto.single.SingleCharacterResponse
import com.halilozcan.animearch.core.data.dto.top.TopAnimeCharacterResponse
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

class RemoteDataSourceImpl @Inject constructor(private val api: AnimeApi) : RemoteDataSource {
override suspend fun getTopAnimeCharacters(): NetworkResponseState<TopAnimeCharacterResponse> {
return try {
val response = api.getTopCharacters()
NetworkResponseState.Success(response)
} catch (e: Exception) {
NetworkResponseState.Error(e)
override fun getTopAnimeCharacters(): Flow<NetworkResponseState<TopAnimeCharacterResponse>> =
flow {
emit(NetworkResponseState.Loading)
try {
val response = api.getTopCharacters()
emit(NetworkResponseState.Success(response))
} catch (e: Exception) {
emit(NetworkResponseState.Error(e))
}
}
}

override suspend fun getSingleCharacter(id: String): NetworkResponseState<SingleCharacterResponse> {
return try {
val response = api.getSingleCharacterFull(id)
NetworkResponseState.Success(response)
} catch (e: Exception) {
NetworkResponseState.Error(e)
override fun getSingleCharacter(id: String): Flow<NetworkResponseState<SingleCharacterResponse>> =
flow {
emit(NetworkResponseState.Loading)
try {
val response = api.getSingleCharacterFull(id)
emit(NetworkResponseState.Success(response))
} catch (e: Exception) {
emit(NetworkResponseState.Error(e))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.halilozcan.animearch.core.data.dto.single.SingleCharacterResponse
import com.halilozcan.animearch.core.data.dto.single.WebpX
import com.halilozcan.animearch.core.data.dto.top.Anime
import com.halilozcan.animearch.core.data.dto.top.TopAnimeCharacterResponse
import com.halilozcan.animearch.core.domain.entity.SingleAnimeEntity
import com.halilozcan.animearch.core.domain.entity.TopAnimeEntity

const val TOP_ANIME_CHARACTERS_RESPONSE_FILE_NAME = "TopAnimeCharactersResponse.json"
const val SINGLE_ANIME_CHARACTER_RESPONSE_FILE_NAME = "SingleAnimeCharacterResponse.json"
Expand Down Expand Up @@ -56,9 +58,35 @@ val topAnimeCharacterResponse =
val singleAnimeCharacterResponse =
SingleCharacterResponse(data = singleAnime)


@VisibleForTesting
const val SERVER_PORT = 8000

@VisibleForTesting
val topAnimeList = listOf(topAnime)

@VisibleForTesting
const val singleAnimePathId = "417"

@VisibleForTesting
const val SERVER_PORT = 8000
val singleAnimeEntity =
SingleAnimeEntity(
id = "417",
name = "Levi",
nameKanji = "ルルーシュ・ランペルージ",
description = "",
imageUrl = "https://cdn.myanimelist.net/images/characters/8/406163.webp",
favorites = 159789
)

@VisibleForTesting
val topAnimeEntity = TopAnimeEntity(
id = "417",
name = "Lelouch Lamperouge",
description = "",
imageUrl = "https://cdn.myanimelist.net/images/characters/8/406163.webp"
)

@VisibleForTesting
val topAnimeEntities = listOf(topAnimeEntity)

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.halilozcan.animearch.core.domain.mapper
package com.halilozcan.animearch.core.data.mapper

import com.halilozcan.animearch.core.data.singleAnime
import com.halilozcan.animearch.core.domain.entity.SingleAnimeEntity
import com.halilozcan.animearch.core.domain.singleAnime
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Before
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.halilozcan.animearch.core.domain.mapper
package com.halilozcan.animearch.core.data.mapper

import com.halilozcan.animearch.core.data.topAnimeList
import com.halilozcan.animearch.core.domain.entity.TopAnimeEntity
import com.halilozcan.animearch.core.domain.topAnimeList
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
Expand Down
Loading

0 comments on commit 8ef850c

Please sign in to comment.