Share
Embed
Android Modularization Recipe
2023년 12월 10일 (일) Devfest Songdo 2023 행사에서의 발표자료입니다.
https://festa.io/events/4246
Sungyong An
December 10, 2023
Transcript
93 slides
-
য়ט ߊח, ৢ೧ ܲ ѐߊ ೯ࢎীࢲ ߊղਊҗ োҙػ ղਊੑפ.
Dagger Hiltী ೧ࢲח ࢸݺೞ ঋणפ. I/O Extended 2023 Seoul DroidKnights 2023 -
A app data domain যڃ ഋక۽ জਸ ѐߊೞҊ աਃ? B
data app domain or app app common C domain app app data app app feature app app app or -
ҳӖীࢲ ӂೞח জ ইఃఫ о٘ (ۨয ܻ࠙, ઓࢿ ੑ, UDF
١) “ۨযܳ যڌѱ ܻ࠙ೡ ࣻ ਸө?” → interface, class ܻ࠙ (ઓࢿ ੑ) “ܻ࠙ܳ ъઁೞח ߑߨ?” → ݽٕച App Architecture Link: h tt ps://developer.android.com/topic/architecture#recommended-app-arch -
ҳӖীࢲ ઁҕೞח ੌ߈ੋ ݽٕച ಁఢ (app, feature, data, common) Modularization
Link: h tt ps://developer.android.com/topic/modularization -
App Architecture — Modularization Modern App Architecture: • UI Layer
• Domain Layer (Optional) • Data Layer → ࢲ۽ 1:1 ݒடغ ঋח. Types of modules: • App Modules • Feature Modules • Data Modules • Common Modules • Test Modules -
→ ਸ тҳೞח ѐߊ “ѱ ݏա…?” Layer Domain Data UI
Feature Domain Data UI ݃ ܲ ӝળਵ۽ ݽٕਸ ա׀ Layer + Feature Domain Data UI -
→ ݽٕചী ױ ೞա হ. “ೠ ӏݽ জী ೠ
ೞա ߑߨ” → ೞ݅ ؊ /ழۆ ࢲ࠺झীࢲח যڌѱ…? Now in Android Link: h tt ps://github.com/android/nowinandroid/blob/main/docs/ModularizationLearningJourney.md -
Recipe “a set of instructions that describes how to prepare
or make something.” (ޖоܳ ળ࠺ೞѢա ݅٘ח ߑߨਸ ࢸݺೞח ੌ۲ ஜ) → উ٘۽٘ জਸ ױ҅ਵ۽ ݽٕചೞח ೞա ߑߨਸ ࢸݺ೧ࠄ. Link: h tt ps://en.wikipedia.org/wiki/Recipe -
ݽٕച ݾ = “ѐߊ ࢤࢿ” “যڃ ҃ী ݽٕചܳ Ҋೞѱ غחо?”
• ѐߊ ࣻо טযաݶࢲ, ࢲ۽ Ҁח ࠗ࠙ ࢤӟ. • ݃ ܲ ߑधਵ۽, ੌࠗ࠙݅ ࢜۽ ӝࣿਸ ࢎਊೞৈ ѐߊೞҊ र. • জ ӝמ טযաݶࢲ, ࢲ۽ ࢚ҙ হח ࠗ࠙ীب औѱ ೱਸ Ҋ ߉ח. • ٘о ݆ইઉࢲ рױೠ ࣻীب ࠽٘दр য়ې Ѧܽ. • নೠ ಬ ಂఠܳ ਗೞח ١ জ ݽٕ ৈ۞ ѐо ػ. → ݽٕച ೯ җ ࣘীࢲب ࢤࢿਸ ਬೞח Ѫਸ ୶ୌೠ. -
(1) Version Catalog ਊೞӝ زੌೠ ۄ࠳۞ܻܳ ৈ۞ ݽٕীࢲ ࢎਊೞѱ ػ.
(ex. Kotlin, Hilt) Version Catalogܳ ਊೞৈ, ۄ࠳۞ܻ ߡਸ ೠ Ҕীࢲ ҙܻೡ ࣻ . → ٘۽٘աஎ 2023 ߊ࢚ ୶ୌ “Gradle Version Catalog 적 용 하 기 ” Link: h tt ps://docs.gradle.org/current/userguide/pla tf orms.html -
// build.gradle dependencies { implementation 'androidx.core:core-ktx:1.9.0' } // gradle/libs.versions.toml [versions]
androidx-core-ktx = "1.9.0" [libraries] androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core-ktx" } // build.gradle dependencies { implementation libs.androidx.core.ktx } Link: h tt ps://developer.android.com/build/migrate-to-catalogs -
(2) Data ݽٕ ܻ࠙ೞӝ о ݢ ࢲߡ, ӝӝ ١ ৻ࠗ৬
ాनೞח ࠗ࠙ਸ ܻ࠙ೞ. • data: API, DB, SharedPreferences ١ ؘఠܳ ܻೞח ݽٕ ◦ Repository, DataSource, DTO, Entity ١ • model: ؘఠ ݽٕীࢲ ৻ࠗী ֢ೞח ݽ؛ ېझܳ ઁҕೞח ݽٕ ◦ ݽ؛ ېझী ࠛਃೠ ٘о ୶оغח Ѫਸ ߑೞ۰ח ݾ (ex. DTO, Enity annotation) ◦ ࣽࣻೠ Java, Kotlin ݽٕب оמ :data :model :app Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#data-modules -
// :data module interface ExampleRepository { suspend fun getExampleList(): List<Example>
} internal class ExampleRepositoryImpl(...) : ExampleRepository { ... } internal interface ExampleRemoteDataSource { ... } internal class ExampleRemoteDataSourceImpl(...) : ExampleRemoteDataSource { ... } internal class ExampleResponse(val something: String) internal interface ExampleLocalDataSource { ... } internal class ExampleLocalDataSourceImpl(...) : ExampleLocalDataSource { ... } internal class ExampleEntity(val something: String) // :model module class Example(val something: String) Repository৬ Model ېझ ৻ীח, internel ఃਕ٘ܳ ਊೞৈ Ӕਸ ઁೠೠ. -
(2) Data ݽٕ ܻ࠙ೞӝ - Legacy DB, SharedPreferences ۢ ӝӝ
۽ஸীࢲ ҙܻ೧ঠ ೞח ࠗ࠙ ೠߣী زೞӝ য۵. interfaceܳ ਊೞৈ पઁ۽ח app ݽٕ ҳഅ۽ োѾ೧فҊ, റী оמೠ ࠗ࠙ࠗఠ ਵ۽ data ݽٕ۽ ӝח ߑߨب оמೞ. Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#data-modules :app :data AppPreferences LegacyRemoteDataSource LegacyPreferences ExampleViewModel ExampleRepository ExampleApi ExamplePreferences ExamplePreferencesImpl ExampleRepositoryImpl internal LegacyLocalDataSource ExampleDatabase -
// :data module interface LegacyPreferences { var something: String }
interface ExamplePreferences : LegacyPreferences { ... } internal class ExamplePreferencesImpl( context: Context, private val legacyPrefs: LegacyPreferences, ) : ExamplePreferences, LegacyPreferences by legacyPrefs { ... } // :app module object AppPreferences : LegacyPreferences { override var something: String = ... } inte rf aceܳ ਊೞৈ, app ݽٕ Singletonਸ data ݽٕী ઁҕೡ ࣻ . -
// :app module @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun
providesLegacyPreferences() : LegacyPreferences { return AppPreferences } } Hiltܳ ਊೞৈ, ઓࢿਸ ࢜۽ inte rf ace۽ ઁҕೡ ࣻ . -
// How to use - val something = AppPreferences.something +
@Inject + lateinit var prefs: ExamplePreferences + + val something = prefs.something ઁ ࢎਊೞ؍ ࠗ࠙ਸ ೞաঀ ജೡ ࣻ . -
(3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ উ٘۽٘ জ ࠽٘ ࠁܳ ನೣೞח ېझ.
• BuildConfig.DEBUG • BuildConfig.VERSION_NAME • BuildConfig.API_BASE_URL (custom) -
// :app module android { defaultConfig { buildConfigField "String", "VERSION_NAME",
“String.valueOf(\"1.0.0\")" } flavorDimensions "default" productFlavors { dev { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://dev...\")" } stage { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://stage...\")" } real { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://real...\")" } } } ࠁా fl avorী ٮۄ ч ׳ۄח ࢎਊ ٘ܳ ୶оೞৈ ࢎਊೠ. Link: h tt ps://developer.android.com/build/gradle-tips#share-custom- fi elds-and-resource-values-with-your-app-code -
(3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ উ٘۽٘ জ ࠽٘ ࠁܳ ನೣೞח ېझ.
• BuildConfig.DEBUG • BuildConfig.VERSION_NAME • BuildConfig.API_BASE_URL (custom) data ݽٕীࢲب BuildConfigܳ ࢎਊ೧ঠ ೠݶ: (1) app ݽٕ ٘ܳ Ӓ۽ ࠂࠢೠ. -
// :app module import soup.movie.BuildConfig // :data module import soup.movie.data.BuildConfig
... ࠛਃೞѱ BuildCon fi g ېझ ࣻо טযդ. -
(3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ উ٘۽٘ জ ࠽٘ ࠁܳ ನೣೞח ېझ.
• BuildConfig.DEBUG • BuildConfig.VERSION_NAME • BuildConfig.API_BASE_URL (custom) data ݽٕীࢲب BuildConfigܳ ࢎਊ೧ঠ ೠݶ: (1) app ݽٕ ٘ܳ Ӓ۽ ࠂࠢೠ. (2) BuildConfigܳ ݽٕ۽ ܻ࠙ೠ. -
// :app module dependencies { implementation project(':buildconfig') } // :data
module dependencies { implementation project(':buildconfig') } ೧ਛա…? -
// :buildconfig module android { namespace 'soup.movie.buildconfig' defaultConfig { buildConfigField
"String", "VERSION_NAME", “String.valueOf(\"1.0.0\")" } buildFeatures { buildConfig true } flavorDimensions "default" productFlavors { dev { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://dev...\")" } stage { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://stage...\")" } real { buildConfigField "String", "API_BASE_URL", "String.valueOf(\"https://real...\")" } } } ઓࢿਸ ыח ݽٚ ݽٕী fl avorо غח ޙઁ. -
// :data module android { buildFeatures { buildConfig false }
flavorDimensions "default" productFlavors { dev {} dev2 {} stage {} real {} } } ೧Ѿೡ ࣻ ח ߑߨ ਸө…? // :app module android { buildFeatures { buildConfig false } flavorDimensions "default" productFlavors { dev {} dev2 {} stage {} real {} } } -
// :buildconfig-stub module android { namespace 'soup.movie.buildconfig' defaultConfig { buildConfigField
"String", "VERSION_NAME", "String.valueOf(\"0\")" buildConfigField "String", "API_BASE_URL", "String.valueOf(\"\")" } buildFeatures { buildConfig true } // productFlavors X } (1) fl avorо হח, stub ݽٕਸ ୶оೠ. -
// :app module dependencies { implementation project(':buildconfig') } // :data
module dependencies { compileOnly project(':buildconfig-stub') } (2) fl avorо ח app ݽٕ ৻ীח stubਸ compileOnly۽ ࢶೠ. -
(3) BuildConfigܳ ݽٕ۽ ܻ࠙ೞӝ compileOnlyח ஹੌ दী݅ ࢎਊغҊ, ࠽٘ Ѿҗޛীח
ನೣغ ঋח. , ࠽٘ Ѿҗޛীח buildconfig-stub ইצ, buildconfig۽ Үغয ನೣػ. ܳ ਊೞৈ, ܲ ݽٕী flavorо غח Ѫਸ ݄ਸ ࣻ . :data :app :buildcon fi g :buildcon fi g-stub compileOnly implementation = :others … Link: h tt ps://developer.android.com/build/dependencies#dependency_con fi gurations -
Milestone 1. :app :data :buildcon fi g :buildcon fi g-stub
:model compileOnly ߣীח app ݽٕীࢲ feature ݽٕਸ աׇࠁ. -
Feature Core ݽٕ ܻ࠙ೞӝ (?) feature ݽٕ݃ ҕాਵ۽ ਃೠ Ѫਸ
ઁҕೞח ݽٕ. • ܻࣗझ: color, dimen, drawable, string, style, … • Base ېझ: Activity, Fragment, Dialog, … • Widget, ഛೣࣻ, ۽٬, ۽Ӓੋ • …١١… → গݒೞݶ ކٶ core۽ ٜযр. ژ ܲ गಌ জ ݽٕਸ ݅٘ח ߑधۄ ୶ୌೞ ঋח. ӒܻҊ data ݽٕীࢲب ਃೞݶ? :app :feature-core :data :feature:search :model api ? -
ৈ۞ ചݶীࢲ ࢎਊೞח ٘ܳ, ҙबࢎী ٮۄ ܻ࠙ೠ. •
kotlin: Kotlin, Coroutines ҕా ٘ • logger: Log • designsystem: ٣ੋ दझమ • resources: ܻࣗझ • … (4) Common ݽٕ ܻ࠙ೞӝ Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#common-modules :common :data :feature:search :kotlin :logger :resources :designsystem api … -
ӝઓীח ઓೞח ݽٚ ݽٕ ܻࣗझ IDо R ېझী ನೣػ. non-transitive
R ېझܳ ࢎਊೞݶ, R ېझ ӝо যٚ. (࠽٘ࣘب ߂ জ ӝ ѐࢶ) ױ, ܻࣗझо ನೣغয ח ഛೠ R ېझܳ import ೧ঠ ೠ. (4) Common ݽٕ ܻ࠙ೞӝ - resources Link: h tt ps://developer.android.com/build/optimize-your-build#use-non-transitive-r-classes com.myapp.library2.R welcome_text = Hi, Songdo com.myapp.library1.R welcome_text = Hi, Songdo goodbye_text = Bye :library2 module welcome_text = Hi, Songdo :library1 module goodbye_text = Bye com.myapp.library.R welcome_text = Hi, Songdo com.myapp.library1.R goodbye_text = Bye non-transitive R classes -
// gradle.properties android.nonTransitiveRClass=true // :library2 module import com.myapp.library2.R val bar
= R.string.welcome_text // :library1 module import com.myapp.library1.R - val foo = R.string.welcome_text + val foo = com.myapp.library2.R.string.welcome_text val bar = R.string.goodbye drawable, string, color ١ ܻࣗझܳ ೞա ݽٕী فݶ…? Link: h tt ps://medium.com/androiddevelopers/5-ways-to-prepare-your-app-build-for-android-studio- fl amingo-release-da34616bb946 -
// gradle.properties android.nonTransitiveRClass=true // :library2 module - import com.myapp.library2.R +
import com.myapp.resources.R val bar = R.string.welcome_text // :library1 module - import com.myapp.library1.R + import com.myapp.resources.R val foo = R.string.welcome_text val bar = R.string.goodbye impo r ܳ ҙܻೞӝ ಞೞ. Link: h tt ps://medium.com/androiddevelopers/5-ways-to-prepare-your-app-build-for-android-studio- fl amingo-release-da34616bb946 -
ܻࣗझܳ ݽٕ۽ ܻ࠙ೡ ٸ, ܻࣗझ ܴ زੌೞݶ overrideغח ޙઁо ਸ
ࣻ . যڃ ࣽࢲ۽ override غযঠ ೞח জীࢲ Ѿೡ ࣻ হ. (4) Common ݽٕ ܻ࠙ೞӝ - resources Link: h tt ps://developer.android.com/studio/projects/android-library#Considerations com.myapp.library2.R welcome_text = Hi, Incheon com.myapp.library1.R welcome_text = Hi, Incheon :library2 module welcome_text = Hi, Songdo :library1 module welcome_text = Hi, Incheon -
// :library2 module android { resourcePrefix 'myapp_lib2_' } <string name="welcome_text">Hi,
Songdo</string> // :library1 module android { resourcePrefix 'myapp_lib1_' } <string name="welcome_text">Hi, Incheon</string> // :app module val foo = R.string.myapp_lib2_welcome_text // Hi, Songdo val bar = R.string.myapp_lib1_welcome_text // Hi, Incheon (1) resourcePre fi xܳ ਊೞח ߑߨ. ೞ݅ ৻ࠗ ۄ࠳۞ܻীࢲ Ҁݶ…? Link: h tt ps://developer.android.com/reference/tools/gradle-api/8.2/com/android/build/api/dsl/CommonExtension#resourcePre fi x() -
// 3rd party library <string name="app_name">3rd Party Library</string> // :common:resources
module <string name="app_name">MyApp</string> // :app module android { sourceSets { main.res.srcDirs += '../common/resources/src/main/res' } } val foo = R.string.app_name // MyApp (2) জ ݽٕ sourceSetsী ܻࣗझ ݽٕ ಫ؊ܳ ୶оೞח ߑߨ. Link: h tt ps://developer.android.com/build/build-variants#con fi gure-sourcesets -
(5) Convention Plugins ࢎਊೞӝ ݽٕ рী ࠽٘ ۽ਸ ҕਬೞח ߑߨ.
ݽٕਸ ؊ ݆ ٜ݅ѱ غחؘ, ݽٕਸ ࢤࢿೞח ࠗ + पࣻܳ ੌ ࣻ . → ٘۽٘աஎ 2023 ߊ࢚ ୶ୌ “Gradle Kotlin ஶ߮࣌ ۞Ӓੋਵ۽ ബਯਵ۽ ݣ౭ ݽٕ ҙܻೞӝ” Link: h tt ps://docs.gradle.org/current/samples/sample_convention_plugins.html -
plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' } android { compileSdk
34 defaultConfig { minSdk 21 targetSdk 34 } compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { jvmTarget = '11' } } plugins { id 'example.android.library' } ߈ࠂغח ٘ܳ ੌ ࣻ . -
plugins { id 'com.google.dagger.hilt.android' id 'org.jetbrains.kotlin.kapt' } dependencies { implementation
libs.hilt.android kapt libs.hilt.compiler } plugins { id 'example.android.hilt' } ݽٕ݃ pluginਸ ٜ݅ӝࠁח, ഝਊೡ ࣻ ח ױਤ۽ ա־ח Ѫ જ. -
// :feature:search module plugins { id 'example.android.library' id 'example.android.hilt' }
dependencies { implementation project(':common:kotlin') implementation project(':common:logger') implementation project(':common:designsystem') implementation project(':common:resources') implementation project(':data') implementation project(':model') } // :feature:search module plugins { id 'example.android.feature' } ա־ݶ ݻ ݅ਵ۽ ୡӝചೡ ࣻ Ҋ, ޘযࢲ ࢎਊೡ ࣻب . -
ੌ߈ਵ۽ ౠ दաܻয়, ӝמী োҙػ ചݶਸ ೞח ݽٕ. • home:
ക • detail: ࢚ࣁ • search: Ѩ࢝ • settings: ࢸ • navigator: ചݶ ز • … (6) Feature ݽٕ ܻ࠙ೞӝ Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#feature-modules :feature :app :home :search :se tt ings :data :navigator :detail -
Feature ݽٕਸ ܻ࠙ೡ ٸ, ചݶ р زೡ ࣻ ח ߑߨ
ਃೞ. (6) Feature ݽٕ ܻ࠙ೞӝ - navigator Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#feature-modules :feature :app :home :detail .DetailActivity .HomeActivity -
// :app module <manifest> <application> <activity android:name=".DetailActivity"/> </application> </manifest> //
:others module startActivity(Intent(context, DetailActivity::class.java) э ݽٕীࢲח Activityܳ ଵઑೡ ࣻ ݅, ܲ ݽٕۄݶ…? -
// :feature:detail module <manifest> <application> <activity android:name=".DetailActivity" android:exported="true"> <intent-filter> ...
<data android:scheme="..." android:host="detail" /> </intent-filter> </activity> </application> </manifest> // :feature:home module startActivity(Intent(ACTION_VIEW, Uri.parse("...://detail")) ٩݂ب оמೞ. ೞ݅ ৻ࠗীب Activityо ֢ػ. -
// :feature:detail module <manifest> <application> <activity android:name=".DetailActivity" android:exported="true"> <intent-filter> ...
<data android:scheme="..." android:host="detail" /> </intent-filter> </activity> </application> </manifest> // :feature:home module startActivity(Intent(ACTION_VIEW, Uri.parse("...://detail?id=...")) ӒܻҊ ID э чਸ ׳೧ঠ ೠݶ…? -
:app Feature ݽٕਸ ܻ࠙ೡ ٸ, ചݶ р زೡ ࣻ ח
ߑߨ ਃೞ. (6) Feature ݽٕ ܻ࠙ೞӝ - navigator Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#feature-modules :feature :home :detail .DetailActivity .HomeActivity :navigator Navigator NavigatorImpl -
// :feature:navigator module sealed interface Destination class Detail(val id: String)
: Destination interface Navigator { fun createIntent(destination: Destination): Intent } // :app module class NavigatorImpl(private val context: Context) : Navigator { override fun createIntent(destination: Destination) { return when (destination) { is Detail -> Intent(context, DetailActivity::class.java) .putExtra(DetailActivity.EXTRA_ID, destination.id) ... } } } Activityী Ӕೡ ࣻ ח Ҕী ҳഅܳ ݅ٚ. -
// :feature:detail module <manifest> <application> <activity android:name=".DetailActivity" android:exported="false"/> </application> </manifest>
// :feature:home module startActivity(navigator.createIntent(Detail(id = ...))) ৻ࠗী Activityܳ ֢ೞ ঋҊ, ݽٕ р ചݶਸ زೡ ࣻ . -
Activity৬ ׳ܻ, Fragmentח ࢤࢿೡ ࣻ ח ߑߨ ਃೞ. :app (6)
Feature ݽٕ ܻ࠙ೞӝ - navigator :feature :home :detail .DetailFragment .HomeFragment :navigator FragmentFactory FragmentFactoryImpl Link: h tt ps://developer.android.com/guide/navigation/navigation-multi-module “Jetpack Navigation ೞױ ݂ܳ ଵҊೞࣁਃ.” -
// :feature:navigator module sealed interface FragmentDestination class Detail(val id: String)
: FragmentDestination interface FragmentFactory { fun createFragment(destination: FragmentDestination): Fragment } // :app module class FragmentFactoryImpl : FragmentFactory { override fun createFragment(destination: FragmentDestination): Fragment { return when (destination) { is Detail -> DetailFragment.newInstance(id = destination.id) ... } } } Fragmentী Ӕೡ ࣻ ח Ҕী ҳഅܳ ݅ٚ. -
// :feature:detail module class DetailFragment : Fragment() { ... companion
object { fun newInstance(id: String): DetailFragment { ... } } } // :feature:home module fragmentManager.commit { val fragment = navigator.createFragment(Detail(id = ...)) add(R.id.fragment_view_container, fragment) } ݽٕ рী Fragmentܳ ҕਬೞৈ ࢎਊೡ ࣻ . -
Milestone 2. :common :app :feature:navigator :data :common :designsystem :feature:search :logger
:buildcon fi g :buildcon fi g-stub :resources :model compileOnly … -
ݽٕച ݾ • ઓࢿ Ѻܻ • ࠽٘दр ѐࢶ → ੜ
غҊ ਸө? Link: h tt ps://developer.android.com/topic/modularization :app :feature:se tt ings :feature:search :feature:navigator :data -
ઓࢿ Ѻܻ ݽٕ ৻ࠗীࢲ Ӕೡ ࣻ হب۾ internal ఃਕ٘ܳ
୶оೞח ߑߨ → ѐߊо ֬ӝ औ. (पࣻ ਬߊ) :feature:search :data interface SearchRepository internal class SearchRepositoryImpl( ... ) : SearchRepository { ... } @Module @InstallIn(SingletonComponent::class) internal interface DataModule { @Provids fun provideSearchRepository(...) : SearchRepository { ... } } @HiltViewModel class SearchViewModel @Inject constructor( private val repository: SearchRepository, ) : ViewModel() { ... } Link: h tt ps://kotlinlang.org/docs/visibility-modi fi ers.html#modules -
ઓࢿ Ѻܻ Ӓېࢲ Domain Layerী ೞח ݽٕਸ ୶оೞৈ, Data ݽٕ
ઓࢿਸ ܻ࠙ೡ ࣻب . → ױࣽೠ ೣࣻ ഐীب UseCaseܳ ୶о೧ঠ ೠ. (࠺ബਯࢿ) UI Layer Domain Layer Data Layer Link: h tt ps://developer.android.com/topic/architecture/domain-layer#data-access-restriction -
// :ui module class ExampleViewModel @Inject constructor( private val usecase:
ExampleUseCase, private val repository: ExampleRepository // X ) { ... } // :domain module class ExampleUseCase( private val repository: ExampleRepository ) { ... } // :data module interface ExampleRepository { ... } internal class ExampleRepositoryImpl(...) : ExampleRepository { ... } :ui :domain :data -
ઓࢿ Ѻܻ Data ݽٕ Domain ݽٕਸ ߄ۄࠁب۾ ߸҃ೞח ߑߨ? (DIP)
→ app ݽٕীࢲח ৈ Ӕೡ ࣻ . (गಌ জ ݽٕۄݶ…) ѾҴ impl ېझܳ ऀӝ۰ݶ, internal ఃਕ٘ܳ ࢎਊ೧ঠ ೠ. Link: h tt ps://developer.android.com/topic/architecture/domain-layer#data-access-restriction UI Layer Domain Layer Data Layer App -
// :ui module class ExampleViewModel @Inject constructor( private val usecase:
ExampleUseCase, private val repository: ExampleRepository // O ) { ... } // :domain module class ExampleUseCase @Inject constructor( private val repository: ExampleRepository ) { ... } interface ExampleRepository { ... } // :data module internal class ExampleRepositoryImpl(...) : ExampleRepository { ... } Domain ݽٕী Dataо ೣԋ ח Ѫ ݏਸө…? :ui :domain :data -
ઓࢿ Ѻܻ ݽٕਸ -api, -impl۽ ܻ࠙ೞݶ, internal ఃਕ٘ হب Ӕਸ
ઁೠೡ ࣻ . runtimeOnlyਸ ਊೞৈ, ஹੌ दীח -impl ݽٕী Ӕೡ ࣻ হ݅ ࠽٘ Ѿҗޛীח ನೣغب۾ ೡ ࣻ . :feature:search :data-api :data-impl runtimeOnly Link: h tt ps://developer.android.com/build/dependencies#dependency_con fi gurations -
// :feature:search module dependencies { implementation project(':data-api') runtimeOnly project(':data-impl') }
// :data-impl module dependencies { implementation project(':data-api') } // :data-api module dependencies { } :feature:search :data-impl :data-api runtimeOnly -
ࣽജ ଵઑ ؊ աইо ݽٕਸ ܻ࠙ೞ ࠁݶ, ݽٕ р ࣽജ
ଵઑо ߊࢤೞח ҃о . :feature :se tt ings :feature-core ࣽജ ଵઑ Link: h tt ps://en.wikipedia.org/wiki/Circular_dependency -
// :feature:core module abstract class BaseActivity : AppCompatActivity { @Inject
lateinit var getCurrentLanguage: GetCurrentLanguageUseCase } // :feature:settings module interface GetCurrentLanguageUseCase { operator fun invoke(): Language } class LanguageSettingActivity : BaseActivity() { ... } :feature ܳ ٜয, Feature ݽٕী UI৬ Domain ۨযо ҕઓೞח ҃. :feature-core :se tt ings -
ࣽജ ଵઑ ҃ח Domain ݽٕਸ ݅٘ח Ѫب ೞա ߑߨ.
→ ܲ Feature ݽٕী UIܳ ઁҕೞҊ रݶ? :feature :se tt ings :feature-core :domain -
ࣽജ ଵઑ ࢜۽ ݽٕਸ ୶оೞח ߑߨ…? → ܴ ӝо о
য۵… दۄب ӝઓ ݽٕ ݺடਸ ߄Բݶ diffо ழ. (Conflict !) :new-ui :feature :se tt ings :feature-core :domain -
ࣽജ ଵઑ ݽٕਸ -api, -impl۽ ܻ࠙ೞݶ, ࣽജ ଵઑܳ ࣚऔѱ ݄ਸ
ࣻ . Link: h tt ps://en.wikipedia.org/wiki/Circular_dependency :feature-core :feature :se tt ings-api :app :se tt ings-impl runtimeOnly -
// :feature-common module abstract class BaseActivity : AppCompatActivity { @Inject
lateinit var getCurrentLanguage: GetCurrentLanguageUseCase } // :feature:settings-api module interface GetCurrentLanguageUseCase { operator fun invoke(): Language } // :feature:settings-impl module class GetCurrentLanguageUseCaseImpl : GetCurrentLanguageUseCase { ... } class LanguageSettingActivity : BaseActivity() { ... } :feature-core :feature :se tt ings-api :se tt ings-impl impl ݽٕ api ݽٕਸ ଵઑೞҊ, ਵ۽ח ೡ ࣻ হ. -
࠽٘दр ѐࢶ ݽٕਸ ա־ݶ ࠽٘दр যٚ…? → ݽٕചೡ ٸ,
݃ೞӝ ए ೣ. ߽۳۽ ࠽٘ೞ ঋਵݶ যٜ ঋח. :app :feature-core :data -
࠽٘दр ѐࢶ ݽٕ р ઓࢿ হݶ, ߽۳۽ ࠽٘ೡ ࣻ .
ࣻࢎ೦ ਸ ٸীب ߸҃ػ ݽٕਸ ଵઑೞח ݽٕٜ݅ द ࠽٘ೠ. :app :feature:search :feature-core :feature:navigator :data -
࠽٘दр ѐࢶ → അपਵ۽ח ઓࢿਸ ыӝ औ. ো ࠽٘दр যٜ
ঋח. Link: h tt ps://github.com/android/nowinandroid/issues/707 :app :feature:search :feature-core :feature:navigator :data -
࠽٘दр ѐࢶ ݽٕਸ -api, -impl۽ ܻ࠙ೞҊ -impl app ݽٕীࢲ݅ ଵઑೞب۾
ೞݶ, -impl ݽٕ р ઓࢿਸ Րযյ ࣻ . :app :feature:search-impl :feature-core :feature:navigator-impl :data :feature:search-api :feature:navigator-api runtimeOnly runtimeOnly -
࠽٘दр ѐࢶ → ৈӝࢲ data ݽٕী ߸҃ࢎ೦ ਵݶ…? ކٶ द
࠽٘೧ঠ ೠ. :app :feature:search-impl :feature-core :feature:navigator-impl :data :feature:search-api :feature:navigator-api runtimeOnly runtimeOnly -
࠽٘दр ѐࢶ data ݽٕب -api, -impl۽ ܻ࠙ೞݶ ػ. → ୭Ӕ
উ٘۽٘ ҕध ޙࢲীب ݽٕച о٘о সؘغ. Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#how_to_implement :data-impl :app :feature:search-impl :feature-core :feature:navigator-impl :data-api :feature:search-api :feature:navigator-api runtimeOnly runtimeOnly -
(7) Feature ݽٕ ܻ࠙ೞӝ ܲ ݽٕীࢲ ਃೠ Ѫਸ ୭ࣗೠ݅ ֢ೞח
ഋక۽ ܻ࠙ೠ. -api: • UseCase interface • UI (Widget, ViewHolder ١) -impl: • UseCase class • UI (Activity, Fragment, ViewModel ١) :feature :app :search-impl :se tt ings-impl :se tt ings-api UseCase :search-api -
runtimeOnly۽ ੋ೧, জ ݽٕীࢲח ؊࢚ Activityী Ӕೡ ࣻ হ. (7)
Feature ݽٕ ܻ࠙ೞӝ - navigator :app :feature :home-impl :detail-impl .DetailActivity .HomeActivity :navigator Navigator NavigatorImpl -
Hiltܳ ਊೞৈ, Activityо ח пп -impl ݽٕীࢲ -NavigatorImplਸ ઁҕೞݶ ػ.
:feature :navigator-impl :home-impl :detail-impl DetailNavigatorImpl HomeNavigatorImpl :navigator-api Navigator NavigatorImpl (7) Feature ݽٕ ܻ࠙ೞӝ - navigator HomeNavigator DetailNavigator -
// :feature:navigator module sealed interface Destination class Detail(val id: String)
: Destination // :app module class NavigatorImpl(private val context: Context) : Navigator { override fun createIntent(destination: Destination) { return when (destination) { is Detail -> Intent(context, DetailActivity::class.java) // X ... } } } জ ݽٕীࢲח -impl ݽٕ Activityী Ӕೡ ࣻ হ. -
// :feature:navigator-api module interface DetailNavigator { fun createIntent(destination: Detail): Intent
} // :feature:detail-impl module class DetailNavigatorImpl(private val context: Context) : DetailNavigator { override fun createIntent(destination: Detail): Intent { return Intent(context, DetailActivity::class.java) // O } } // :feature:navigator-impl module class NavigatorImpl(private val detailNavigator: DetailNavigator) : Navigator { override fun createIntent(destination: Destination) { ... } } Activityо ח -impl ݽٕীࢲ -NavigatorImplਸ ҳഅೞݶ ػ. -
(8) Data ݽٕ ࣁ࠙ചೞӝ ৈ۞ ࢎۈ زदী ೞա ݽٕਸ ࣻೠ.
→ ߽ݾ, Conflict оמࢿ ߽۳۽ ѐߊೞחؘ ߑ೧غח ࠗ࠙ ݶ ৈաо. :data :home-impl :detail-impl :search-impl :database-impl :network-impl model … :home-api :detail-api :search-api :network-api :database-api -
Milestone 3. :data :feature :navigator-impl :navigator-api :common :app :common :designsystem
:logger :buildcon fi g :buildcon fi g-stub :resources compileOnly … :search-impl :search-api :detail-impl :detail-api :search-api :search-impl :detail-api :detail-impl … -
زदী ࣻೞݶ Git Merge Conflictо ߊࢤೠ. ӏݽо ழࣻ۾ ۠
ٜࠗ࠙ਸ ୭ࣗചೞח Ѫ જ. ܳ ٜݶ, Applicationীࢲ ୡӝചೞח Ѫٜ. :app ߽ݾ ӝ :feature :home HomeData MyApplication :search SearchData -
// :app module class MyApplication : Application() { override fun
onCreate() { ... HomeData.preload() + SearchData.preload() ... } } ࢲ۽ ܲ PRীࢲ ୡӝച ೣࣻܳ ୶оೞݶ, Con fl ict ߊࢤೠ. -
Dagger Hiltܳ ਊೞৈ app ݽٕীࢲ feature ݽٕী Ӕೞח Ѫ
ইצ, feature ݽٕ app ݽٕী ઁҕೞب۾ ߸҃ೞݶ ػ. :app ߽ݾ ӝ :feature :home-impl HomeInitializer :sta rt up-api Initializer Set<Initializer> :search-impl SearchInitializer -
// :feature:search-impl module @Module @InstallIn(SingletonComponent::class) interface SearchModule { @IntoSet @Binds
fun bindsInitializer(impl: SearchInitializer): Initializer } class SearchInitializer @Inject constructor() : Initializer { override operator fun invoke() { SearchData.preload(...) } } // :app module @HiltAndroidApp class MyApplication : Application() { @Inject lateinit var initializerSet: Set<@JvmSuppressWildcards Initializer> } // :feature:startup-api module interface Initializer { operator fun invoke() } -
Link: h tt ps://developer.android.com/topic/modularization/pa tt erns#app-modules জ ੌࠗ ӝמ݅ਸ ನೣೞח
জ ݽٕ. ౠ ߈ࠂਵ۽ ࠽٘೧ঠ ೞח ѐߊ ױ҅ীࢲ ࣗਃغח दрਸ ױ୷ೡ ࣻ . (9) ࢠ জ ݽٕ ୶оೞӝ … … … … -
Milestone 4. :data :feature :navigator-impl :navigator-api :common :app :common :designsystem
:logger :buildcon fi g :buildcon fi g-stub :resources compileOnly … :search-impl :search-api :detail-impl :detail-api :search-api :search-impl :detail-api :detail-impl … :search-sample :detail-sample :designsystem-sample -
ܻ • Data, Common, Feature ࣽਵ۽ ױ҅ਵ۽ ݽٕച ೡ ࣻ
. • ٘ܳ ҕాചೞৈ, ݽٕ ୡӝച ࠺ਊਸ ח Ѫ જ. ◦ Version Catalog, Convention Plugins ١ • compileOnly + stubਸ ਊೞৈ, flavor ܳ ݄ਸ ࣻ . (ex. BuildConfig) • -api, -impl ݽٕ۽ ܻ࠙ೞৈ, ৈ۞о ਸ оઉৢ ࣻ . ◦ ઓࢿ Ѻܻ, ࣽജଵઑ, ࠽٘दр ١ • runtimeOnlyܳ ਊೞৈ, app ݽٕীࢲ ߄ۄࠁח ઓࢿਸ ੌ ࣻ . • ݽٕਸ ܻ࠙ೠҊ ޖઑѤ ࢤࢿ ֫ই ঋח. ࢤࢿਸ ֫ੌ ࣻ ח ߑߨਸ Ҋೞݴ ݽٕച೧ঠ ೠ. ◦ ߽ݾ, Conflict ӝ ١