Share
Embed
Clean Architecture (in Android) Revised
[Droid Knights 2019] 행사에서 발표한 [Clean Architecture (in Android) Revised] 발표 자료입니다. 이전에 발표했던 [Clean Architecture in Android] 발표를 2019년의 시각으로 재구성을 시도합니다.
Sunghyun Hwang
April 05, 2019
Transcript
91 slides
-
<6TF$BTF>#VZ(PPET #VZFSDBMMTJOXJUIBQVSDIBTFSFRVFTU $PNQBOZDBQUVSFTCVZFSTOBNF BEESFTT SFRVFTUFEHPPET FUD $PNQBOZHJWFTCVZFSJOGPSNBUJPOPOHPPET
QSJDFT EFMJWFSZ EBUFT FUD #VZFSTJHOTGPSPSEFS IUUQXXXDTPUBHPBDO[DPVSTFXPSLDPTDVDUFNQMBIUN -
<6TF$BTF>#VZ(PPET #VZFSDBMMTJOXJUIBQVSDIBTFSFRVFTU $PNQBOZDBQUVSFTCVZFSTOBNF BEESFTT SFRVFTUFEHPPET FUD $PNQBOZHJWFTCVZFSJOGPSNBUJPOPOHPPET
QSJDFT EFMJWFSZ EBUFT FUD #VZFSTJHOTGPSPSEFS IUUQXXXDTPUBHPBDO[DPVSTFXPSLDPTDVDUFNQMBIUN -
<6TF$BTF>#VZ(PPET #VZFSDBMMTJOXJUIBQVSDIBTFSFRVFTU $PNQBOZDBQUVSFTCVZFSTOBNF BEESFTT SFRVFTUFEHPPET FUD $PNQBOZHJWFTCVZFSJOGPSNBUJPOPOHPPET
QSJDFT EFMJWFSZ EBUFT FUD #VZFSTJHOTGPSPSEFS IUUQXXXDTPUBHPBDO[DPVSTFXPSLDPTDVDUFNQMBIUN -
<6TF$BTF>#VZ(PPET #VZFSDBMMTJOXJUIBQVSDIBTFSFRVFTU $PNQBOZDBQUVSFTCVZFSTOBNF BEESFTT SFRVFTUFEHPPET FUD $PNQBOZHJWFTCVZFSJOGPSNBUJPOPOHPPET
QSJDFT EFMJWFSZ EBUFT FUD #VZFSTJHOTGPSPSEFS IUUQXXXDTPUBHPBDO[DPVSTFXPSLDPTDVDUFNQMBIUN -
<6TF$BTF>#VZ(PPET #VZFSDBMMTJOXJUIBQVSDIBTFSFRVFTU $PNQBOZDBQUVSFTCVZFSTOBNF BEESFTT SFRVFTUFEHPPET FUD $PNQBOZHJWFTCVZFSJOGPSNBUJPOPOHPPET
QSJDFT EFMJWFSZ EBUFT FUD #VZFSTJHOTGPSPSEFS IUUQXXXDTPUBHPBDO[DPVSTFXPSLDPTDVDUFNQMBIUN -
<6TF$BTF>#VZ(PPET #VZFSDBMMTJOXJUIBQVSDIBTFSFRVFTU $PNQBOZDBQUVSFTCVZFSTOBNF BEESFTT SFRVFTUFEHPPET FUD $PNQBOZHJWFTCVZFSJOGPSNBUJPOPOHPPET
QSJDFT EFMJWFSZ EBUFT FUD #VZFSTJHOTGPSPSEFS IUUQXXXDTPUBHPBDO[DPVSTFXPSLDPTDVDUFNQMBIUN -
External Interfaces Android UI DB Network W eb iOS Cache
Controllers G atew ays Presenters Use Cases Entities 5IF$MFBO "SDIJUFDUVSF -
External Interfaces Android UI DB Network W eb iOS Cache
Use Cases Entities &OUJUZ⛓ \%# )551"1* ^ -
External Interfaces Android UI DB Network W eb iOS Cache
Use Cases Entities &OUJUZ⛓ \%# )551"1* ^ -
External Interfaces Android UI DB Network W eb iOS Cache
Controllers G atew ays Presenters Use Cases Entities *OUFSGBDF "EBQUFST -
External Interfaces Android UI DB Network W eb iOS Cache
Controllers G atew ays Presenters Use Cases Entities 4FQBSBUF #VTJOFTT-PHJD GSPN UIF$IBOHFBCMF -
External Interfaces Android UI DB Network W eb iOS Cache
Controllers G atew ays Presenters Use Cases Entities 5IF %FQFOEFODZ 3VMF -
IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM $PODMVTJPO $POGPSNJOHUPUIFTFTJNQMFSVMFTJTOPUIBSE BOEXJMMTBWFZPVBMPUPGIFBEBDIFT HPJOH GPSXBSE #Z TFQBSBUJOH UIF TPGUXBSF
JOUP MBZFST BOE DPOGPSNJOH UP 5IF %FQFOEFODZ3VMF ZPVXJMMDSFBUFBTZTUFNUIBUJTJOUSJOTJDBMMZUFTUBCMF XJUIBMMUIF CFOFGJUTUIBUJNQMJFT8IFOBOZPGUIFFYUFSOBMQBSUTPGUIFTZTUFNCFDPNFPCTPMFUF MJLFUIFEBUBCBTF PSUIFXFCGSBNFXPSL ZPVDBOSFQMBDFUIPTFPCTPMFUFFMFNFOUT XJUIBNJOJNVNPGGVTT -
IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM $PODMVTJPO $POGPSNJOHUPUIFTFTJNQMFSVMFTJTOPUIBSE BOEXJMMTBWFZPVBMPUPGIFBEBDIFT HPJOH GPSXBSE #Z TFQBSBUJOH UIF TPGUXBSF
JOUP MBZFST BOE DPOGPSNJOH UP 5IF %FQFOEFODZ3VMF ZPVXJMMDSFBUFBTZTUFNUIBUJTJOUSJOTJDBMMZUFTUBCMF XJUIBMMUIF CFOFGJUTUIBUJNQMJFT8IFOBOZPGUIFFYUFSOBMQBSUTPGUIFTZTUFNCFDPNFPCTPMFUF MJLFUIFEBUBCBTF PSUIFXFCGSBNFXPSL ZPVDBOSFQMBDFUIPTFPCTPMFUFFMFNFOUT XJUIBNJOJNVNPGGVTT -
IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM $PODMVTJPO $POGPSNJOHUPUIFTFTJNQMFSVMFTJTOPUIBSE BOEXJMMTBWFZPVBMPUPGIFBEBDIFT HPJOH GPSXBSE #Z TFQBSBUJOH UIF TPGUXBSF
JOUP MBZFST BOE DPOGPSNJOH UP 5IF %FQFOEFODZ3VMF ZPVXJMMDSFBUFBTZTUFNUIBUJTJOUSJOTJDBMMZUFTUBCMF XJUIBMMUIF CFOFGJUTUIBUJNQMJFT8IFOBOZPGUIFFYUFSOBMQBSUTPGUIFTZTUFNCFDPNFPCTPMFUF MJLFUIFEBUBCBTF PSUIFXFCGSBNFXPSL ZPVDBOSFQMBDFUIPTFPCTPMFUFFMFNFOUT XJUIBNJOJNVNPGGVTT -
IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM $PODMVTJPO $POGPSNJOHUPUIFTFTJNQMFSVMFTJTOPUIBSE BOEXJMMTBWFZPVBMPUPGIFBEBDIFT HPJOH GPSXBSE #Z TFQBSBUJOH UIF TPGUXBSF
JOUP MBZFST BOE DPOGPSNJOH UP 5IF %FQFOEFODZ3VMF ZPVXJMMDSFBUFBTZTUFNUIBUJTJOUSJOTJDBMMZUFTUBCMF XJUIBMMUIF CFOFGJUTUIBUJNQMJFT8IFOBOZPGUIFFYUFSOBMQBSUTPGUIFTZTUFNCFDPNFPCTPMFUF MJLFUIFEBUBCBTF PSUIFXFCGSBNFXPSL ZPVDBOSFQMBDFUIPTFPCTPMFUFFMFNFOUT XJUIBNJOJNVNPGGVTT -
IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM $PODMVTJPO $POGPSNJOHUPUIFTFTJNQMFSVMFTJTOPUIBSE BOEXJMMTBWFZPVBMPUPGIFBEBDIFT HPJOH GPSXBSE #Z TFQBSBUJOH UIF TPGUXBSF
JOUP MBZFST BOE DPOGPSNJOH UP 5IF %FQFOEFODZ3VMF ZPVXJMMDSFBUFBTZTUFNUIBUJTJOUSJOTJDBMMZUFTUBCMF XJUIBMMUIF CFOFGJUTUIBUJNQMJFT8IFOBOZPGUIFFYUFSOBMQBSUTPGUIFTZTUFNCFDPNFPCTPMFUF MJLFUIFEBUBCBTF PSUIFXFCGSBNFXPSL ZPVDBOSFQMBDFUIPTFPCTPMFUFFMFNFOUT XJUIBNJOJNVNPGGVTT -
IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM $PODMVTJPO $POGPSNJOHUPUIFTFTJNQMFSVMFTJTOPUIBSE BOEXJMMTBWFZPVBMPUPGIFBEBDIFT HPJOH GPSXBSE #Z TFQBSBUJOH UIF TPGUXBSF
JOUP MBZFST BOE DPOGPSNJOH UP 5IF %FQFOEFODZ3VMF ZPVXJMMDSFBUFBTZTUFNUIBUJTJOUSJOTJDBMMZUFTUBCMF XJUIBMMUIF CFOFGJUTUIBUJNQMJFT8IFOBOZPGUIFFYUFSOBMQBSUTPGUIFTZTUFNCFDPNFPCTPMFUF MJLFUIFEBUBCBTF PSUIFXFCGSBNFXPSL ZPVDBOSFQMBDFUIPTFPCTPMFUFFMFNFOUT XJUIBNJOJNVNPGGVTT *UT*NQMFNFOUBUJPO -
IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM $PODMVTJPO $POGPSNJOHUPUIFTFTJNQMFSVMFTJTOPUIBSE BOEXJMMTBWFZPVBMPUPGIFBEBDIFT HPJOH GPSXBSE #Z TFQBSBUJOH UIF TPGUXBSF
JOUP MBZFST BOE DPOGPSNJOH UP 5IF %FQFOEFODZ3VMF ZPVXJMMDSFBUFBTZTUFNUIBUJTJOUSJOTJDBMMZUFTUBCMF XJUIBMMUIF CFOFGJUTUIBUJNQMJFT8IFOBOZPGUIFFYUFSOBMQBSUTPGUIFTZTUFNCFDPNFPCTPMFUF MJLFUIFEBUBCBTF PSUIFXFCGSBNFXPSL ZPVDBOSFQMBDFUIPTFPCTPMFUFFMFNFOUT XJUIBNJOJNVNPGGVTT 5BPPGUIF$" *UT*NQMFNFOUBUJPO -
class MainActivity : AppCompatActivity(), TodoGatewayImpl, TodoDaoProvider, TodosView { override val
compositeDisposable: CompositeDisposable get() = CompositeDisposable() override val todoDao: TodoDao get() = AppDatabase.getInstance(this).todosDao() override fun renderTodos(vm: TodosViewModel) { someTextView.text = vm.statusDescription } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) fab.setOnClickListener { handleAddTodo(Schedulers.io(), AndroidSchedulers.mainThread()) } handleStart(Schedulers.io(), AndroidSchedulers.mainThread()) } override fun onDestroy() { super.onDestroy() handleDestroy() } } -
class MainActivity : AppCompatActivity(), TodoGatewayImpl, TodoDaoProvider, TodosView { override val
compositeDisposable: CompositeDisposable get() = CompositeDisposable() override val todoDao: TodoDao get() = AppDatabase.getInstance(this).todosDao() override fun renderTodos(vm: TodosViewModel) { someTextView.text = vm.statusDescription } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) fab.setOnClickListener { handleAddTodo(Schedulers.io(), AndroidSchedulers.mainThread()) } handleStart(Schedulers.io(), AndroidSchedulers.mainThread()) } override fun onDestroy() { super.onDestroy() handleDestroy() } } -
class MainActivity : AppCompatActivity(), TodoGatewayImpl, TodoDaoProvider, TodosView { override val
compositeDisposable: CompositeDisposable get() = CompositeDisposable() override val todoDao: TodoDao get() = AppDatabase.getInstance(this).todosDao() override fun renderTodos(vm: TodosViewModel) { someTextView.text = vm.statusDescription } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) fab.setOnClickListener { handleAddTodo(Schedulers.io(), AndroidSchedulers.mainThread()) } handleStart(Schedulers.io(), AndroidSchedulers.mainThread()) } override fun onDestroy() { super.onDestroy() handleDestroy() } } -
class MainActivity : AppCompatActivity(), TodoGatewayImpl, TodoDaoProvider, TodosView { override val
compositeDisposable: CompositeDisposable get() = CompositeDisposable() override val todoDao: TodoDao get() = AppDatabase.getInstance(this).todosDao() override fun renderTodos(vm: TodosViewModel) { someTextView.text = vm.statusDescription } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) fab.setOnClickListener { handleAddTodo(Schedulers.io(), AndroidSchedulers.mainThread()) } handleStart(Schedulers.io(), AndroidSchedulers.mainThread()) } override fun onDestroy() { super.onDestroy() handleDestroy() } } -
class MainActivity : AppCompatActivity(), TodoGatewayImpl, TodoDaoProvider, TodosView { override val
compositeDisposable: CompositeDisposable get() = CompositeDisposable() override val todoDao: TodoDao get() = AppDatabase.getInstance(this).todosDao() override fun renderTodos(vm: TodosViewModel) { someTextView.text = vm.statusDescription } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) fab.setOnClickListener { handleAddTodo(Schedulers.io(), AndroidSchedulers.mainThread()) } handleStart(Schedulers.io(), AndroidSchedulers.mainThread()) } override fun onDestroy() { super.onDestroy() handleDestroy() } } -
interface TodosView : AddTodo, GetTodos, TodosPresenter, TodosController { val compositeDisposable:
CompositeDisposable fun renderTodos(vm: TodosViewModel) fun handleStart(sub: Scheduler, ob: Scheduler) { val d = getTodos() .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } fun handleAddTodo(sub: Scheduler, ob: Scheduler) { val d = addTodo(requestAddTodo(LocalDateTime.now())) .flatMap { getTodos() } .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } } fun handleDestroy() { compositeDisposable.dispose() } -
interface TodosView : AddTodo, GetTodos, TodosPresenter, TodosController { val compositeDisposable:
CompositeDisposable fun renderTodos(vm: TodosViewModel) fun handleStart(sub: Scheduler, ob: Scheduler) { val d = getTodos() .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } fun handleAddTodo(sub: Scheduler, ob: Scheduler) { val d = addTodo(requestAddTodo(LocalDateTime.now())) .flatMap { getTodos() } .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } } fun handleDestroy() { compositeDisposable.dispose() } -
interface TodosView : AddTodo, GetTodos, TodosPresenter, TodosController { val compositeDisposable:
CompositeDisposable fun renderTodos(vm: TodosViewModel) fun handleStart(sub: Scheduler, ob: Scheduler) { val d = getTodos() .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } fun handleAddTodo(sub: Scheduler, ob: Scheduler) { val d = addTodo(requestAddTodo(LocalDateTime.now())) .flatMap { getTodos() } .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } } fun handleDestroy() { compositeDisposable.dispose() } import io.reactivex.Scheduler import io.reactivex.disposables.CompositeDisposable import java.time.LocalDateTime -
interface TodosView : AddTodo, GetTodos, TodosPresenter, TodosController { val compositeDisposable:
CompositeDisposable fun renderTodos(vm: TodosViewModel) fun handleStart(sub: Scheduler, ob: Scheduler) { val d = getTodos() .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } fun handleAddTodo(sub: Scheduler, ob: Scheduler) { val d = addTodo(requestAddTodo(LocalDateTime.now())) .flatMap { getTodos() } .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } } fun handleDestroy() { compositeDisposable.dispose() } -
interface GetTodos : TodoGateway, TodoServices { fun getTodos(): Single<GetTodosResult> =
get().map { with(calculateProgress(it)) { GetTodosResult( it, this.first, this.second, this.third ) } } } -
interface TodoServices { fun calculateProgress(todos: List<Todo>): Triple<Int, Int, Float> {
val nrOfDone = todos.count { it.done } val nrOfTotal = todos.count() return Triple(nrOfDone, nrOfTotal, nrOfDone / nrOfTotal.toFloat()) } } interface GetTodos : TodoGateway, TodoServices { fun getTodos(): Single<GetTodosResult> = get().map { with(calculateProgress(it)) { GetTodosResult( it, this.first, this.second, this.third ) } } } -
interface TodoServices { fun calculateProgress(todos: List<Todo>): Triple<Int, Int, Float> {
val nrOfDone = todos.count { it.done } val nrOfTotal = todos.count() return Triple(nrOfDone, nrOfTotal, nrOfDone / nrOfTotal.toFloat()) } } interface GetTodos : TodoGateway, TodoServices { fun getTodos(): Single<GetTodosResult> = get().map { with(calculateProgress(it)) { GetTodosResult( it, this.first, this.second, this.third ) } } } data class Todo(val id: Long, val description: String, val done: Boolean) -
interface TodoServices { fun calculateProgress(todos: List<Todo>): Triple<Int, Int, Float> {
val nrOfDone = todos.count { it.done } val nrOfTotal = todos.count() return Triple(nrOfDone, nrOfTotal, nrOfDone / nrOfTotal.toFloat()) } } interface GetTodos : TodoGateway, TodoServices { fun getTodos(): Single<GetTodosResult> = get().map { with(calculateProgress(it)) { GetTodosResult( it, this.first, this.second, this.third ) } } } data class Todo(val id: Long, val description: String, val done: Boolean) -
interface GetTodos : TodoGateway, TodoServices { fun getTodos(): Single<GetTodosResult> =
get().map { with(calculateProgress(it)) { GetTodosResult( it, this.first, this.second, this.third ) } } } interface TodoGateway { fun get(): Single<List<Todo>> fun get(id: Int): Maybe<Todo> fun add(description: String, done: Boolean): Single<Todo> } -
class MainActivity : AppCompatActivity(), TodoGatewayImpl, TodoDaoProvider, TodosView { override val
compositeDisposable: CompositeDisposable get() = CompositeDisposable() override val todoDao: TodoDao get() = AppDatabase.getInstance(this).todosDao() override fun renderTodos(vm: TodosViewModel) { someTextView.text = vm.statusDescription } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) fab.setOnClickListener { handleAddTodo(Schedulers.io(), AndroidSchedulers.mainThread()) } handleStart(Schedulers.io(), AndroidSchedulers.mainThread()) } override fun onDestroy() { super.onDestroy() handleDestroy() } } -
interface TodoGatewayImpl : TodoGateway, TodoDaoProvider { private fun convert(d: TodoDataModel):
Todo = Todo(d.id!!, d.description, d.done) override fun get(): Single<List<Todo>> = todoDao.get().map { it.map(::convert) } override fun get(id: Int): Maybe<Todo> = todoDao.get(id).map(::convert) override fun add(description: String, done: Boolean): Single<Todo> = todoDao.insert(TodoDataModel(null, description, done)).map { id -> Todo(id, description, done) } } -
interface TodosView : AddTodo, GetTodos, TodosPresenter, TodosController { val compositeDisposable:
CompositeDisposable fun renderTodos(vm: TodosViewModel) fun handleStart(sub: Scheduler, ob: Scheduler) { val d = getTodos() .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } } -
interface TodosView : AddTodo, GetTodos, TodosPresenter, TodosController { val compositeDisposable:
CompositeDisposable fun renderTodos(vm: TodosViewModel) fun handleStart(sub: Scheduler, ob: Scheduler) { val d = getTodos() .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } } -
interface TodosView : AddTodo, GetTodos, TodosPresenter, TodosController { val compositeDisposable:
CompositeDisposable fun renderTodos(vm: TodosViewModel) fun handleStart(sub: Scheduler, ob: Scheduler) { val d = getTodos() .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } } -
interface TodosView : AddTodo, GetTodos, TodosPresenter, TodosController { val compositeDisposable:
CompositeDisposable fun renderTodos(vm: TodosViewModel) fun handleStart(sub: Scheduler, ob: Scheduler) { val d = getTodos() .subscribeOn(sub) .observeOn(ob) .subscribe { r -> renderTodos( presentTodos(r) ) } compositeDisposable.add(d) } } -
interface TodosPresenter { fun presentTodos(result: GetTodosResult): TodosViewModel = TodosViewModel( result.todos.map
{ TodoViewModel(it.done, it.description.take(maxDescLength)) }, "${result.nrOfDone} / ${result.nrOfTotal}" ) companion object { private const val maxDescLength = 20 } } -
class MainActivity : AppCompatActivity(), TodoGatewayImpl, TodoDaoProvider, TodosView { override fun
renderTodos(vm: TodosViewModel) { someTextView.text = vm.statusDescription } } -
interface TodosController { fun requestAddTodo(dt: LocalDateTime): AddTodoRequest = AddTodoRequest(dt.format(dtFormat), false)
companion object { private val dtFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") } } -
Entity • ࣽࣻೠ Java(Kotlin) ݽٕ • Android৬ ઓࢿ হ •
بݫੋ(࠺ૉפझ ۽)ীࢲ ࢤغח ѐ֛ਸ അ • (э ࢲ࠺झ) Android - iOS - ࢲߡ ݽف زੌೠ ഋక -
Domain • ࣽࣻೠ Java(Kotlin) ݽٕ • Use Case • Repository
Interface -
ߛ࢟۞٘ܳ ݅٘ח ۨפझীࢲ ൞ ৬ ೣԋೡ উ٘۽٘ ূפয ٜ࠙ਸ
Ҋ णפ. Kotlin, RxJava, Retrofit, Glide, Realm -
8FSF)JSJOH "OESPJE&OHJOFFS J04&OHJOFFSJOH 4FSWFS&OHJOFFS 1ZUIPO4DBMB
%FW0QT&OHJOFFS *OGSB&OHJOFFS4ZTUFN&OHJOFFS %BUB&OHJOFFS 1SPEVDU"OBMZTU 2"&OHJOFFS