Slide 1

Slide 1 text

Clean Architecture in Android const val presenter = "sunghyunzz"

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

• 2015֙ 12ਘ ୹द

Slide 5

Slide 5 text

• 2015֙ 12ਘ ୹द • ف ߣ੄ ؀ӏݽ সؘ੉౟

Slide 6

Slide 6 text

• 2015֙ 12ਘ ୹द • ف ߣ੄ ؀ӏݽ সؘ੉౟ • Ր੐হח ࢎਊ੗ ੋఠ࠭ܳ ాೠ ѐࢶ

Slide 7

Slide 7 text

• 2015֙ 12ਘ ୹द • ف ߣ੄ ؀ӏݽ সؘ੉౟ • Ր੐হח ࢎਊ੗ ੋఠ࠭ܳ ాೠ ѐࢶ • ҅ࣘ ѐࢶغҊ ߸ചೞח ઁಿ

Slide 8

Slide 8 text

• 2015֙ 12ਘ ୹द • ف ߣ੄ ؀ӏݽ সؘ੉౟ • Ր੐হח ࢎਊ੗ ੋఠ࠭ܳ ాೠ ѐࢶ • ҅ࣘ ѐࢶغҊ ߸ചೞח ઁಿ • ௏٘о ҅ࣘ ߄Ո׮

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

“߸ച ৻ী ৔ਗೠ Ѫ਷ হ׮.” - Heraclitus

Slide 11

Slide 11 text

“ೖೡ ࣻ হਵݶ ૌѹۄ.” - ־ҵо

Slide 12

Slide 12 text

import kotlinx.android.synthetic.main.activity_transactions_temp.*

Slide 13

Slide 13 text

߸ചী ੜ ؀਽ೡ ࣻ ੓ח ௏٘ח যڃ ݽणੌө?

Slide 14

Slide 14 text

߸ചী ੜ ؀਽ೡ ࣻ ੓׮. = ߸ചী ٮܲ ௏٘੄ ߸҃੉ ੸׮.

Slide 15

Slide 15 text

߸ചী ੜ ؀਽ೡ ࣻ ੓׮. = ߸ചী ٮܲ ௏٘੄ ߸҃੉ ੸׮.

Slide 16

Slide 16 text

߸ചী ੜ ؀਽ೡ ࣻ ੓׮. = ߸ചী ٮܲ ௏٘੄ ߸҃੉ ੸׮. = ௏٘о ੜ ܻ࠙غয੓׮.

Slide 17

Slide 17 text

߸ചী ੜ ؀਽ೡ ࣻ ੓׮. = ߸ചী ٮܲ ௏٘੄ ߸҃੉ ੸׮. = ௏٘о ੜ ܻ࠙غয੓׮. = ௏٘о ࠄ૕ী ݏѱ ࢸ҅غয੓׮.

Slide 18

Slide 18 text

ࠄ૕ = { بݫੋ੄ ࠄ૕, ѐߊ ҳઑ ࢚੄ ࠄ૕, উ٘۽੉٘ ೒ۖಬ੄ ࠄ૕, ... }

Slide 19

Slide 19 text

ࠄ૕ = { بݫੋ੄ ࠄ૕, ѐߊ ҳઑ ࢚੄ ࠄ૕, উ٘۽੉٘ ೒ۖಬ੄ ࠄ૕, ... }

Slide 20

Slide 20 text

ࠄ૕ = { بݫੋ੄ ࠄ૕, ѐߊ ҳઑ ࢚੄ ࠄ૕, উ٘۽੉٘ ೒ۖಬ੄ ࠄ૕, ... }

Slide 21

Slide 21 text

ࠄ૕ = { بݫੋ੄ ࠄ૕, ѐߊ ҳઑ ࢚੄ ࠄ૕, উ٘۽੉٘ ҳઑ ࢚੄ ࠄ૕, ... }

Slide 22

Slide 22 text

ࠄ૕ = { بݫੋ੄ ࠄ૕, ѐߊ ҳઑ ࢚੄ ࠄ૕, উ٘۽੉٘ ҳઑ ࢚੄ ࠄ૕, ... }

Slide 23

Slide 23 text

https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

Slide 24

Slide 24 text

1. Independent of Frameworks

Slide 25

Slide 25 text

1. Independent of Frameworks 2. Testable

Slide 26

Slide 26 text

1. Independent of Frameworks 2. Testable 3. Independent of UI

Slide 27

Slide 27 text

1. Independent of Frameworks 2. Testable 3. Independent of UI 4. Independent of Database

Slide 28

Slide 28 text

1. Independent of Frameworks 2. Testable 3. Independent of UI 4. Independent of Database 5. Independent of External Configuration

Slide 29

Slide 29 text

https://fernandocejas.com/2014/09/03/architecting-android-the-clean-way

Slide 30

Slide 30 text

4 Layers

Slide 31

Slide 31 text

Presentation

Slide 32

Slide 32 text

Presentation Data

Slide 33

Slide 33 text

Presentation Data Domain

Slide 34

Slide 34 text

Presentation Data Domain Entity

Slide 35

Slide 35 text

Presentation Data Domain Entity

Slide 36

Slide 36 text

Entity • ࣽࣻೠ Java(Kotlin) ݽٕ

Slide 37

Slide 37 text

Entity • ࣽࣻೠ Java(Kotlin) ݽٕ • Android৬੄ ੄ઓࢿ੉ হ਺

Slide 38

Slide 38 text

Entity • ࣽࣻೠ Java(Kotlin) ݽٕ • Android৬੄ ੄ઓࢿ੉ হ਺ • بݫੋ(࠺ૉפझ ۽૒)ীࢲ ౵ࢤغח ѐ֛ਸ ಴അ

Slide 39

Slide 39 text

Entity • ࣽࣻೠ Java(Kotlin) ݽٕ • Android৬੄ ੄ઓࢿ੉ হ਺ • بݫੋ(࠺ૉפझ ۽૒)ীࢲ ౵ࢤغח ѐ֛ਸ ಴അ • (э਷ ࢲ࠺झ) Android - iOS - ࢲߡ ݽف زੌೠ ഋక

Slide 40

Slide 40 text

class TransactionCategory(
 val id: String,
 val name: String,
 val transactionType: TransactionType,
 val priority: Int ) : Entity

Slide 41

Slide 41 text

Domain • ࣽࣻೠ Java(Kotlin) ݽٕ

Slide 42

Slide 42 text

Domain • ࣽࣻೠ Java(Kotlin) ݽٕ • Use Case

Slide 43

Slide 43 text

Domain • ࣽࣻೠ Java(Kotlin) ݽٕ • Use Case • Repository Interface ੿੄

Slide 44

Slide 44 text

class GetBanks(val repository: BanksRepository) : UseCase>() {
 override fun buildUseCaseObservable(): Observable> {
 return repository.getAll()
 }
 }

Slide 45

Slide 45 text

class GetBanks(val repository: BanksRepository) : UseCase>() {
 override fun buildUseCaseObservable(): Observable> {
 return repository.getAll()
 }
 }

Slide 46

Slide 46 text

class GetBanks(val repository: BanksRepository) : UseCase>() {
 override fun buildUseCaseObservable(): Observable> {
 return repository.getAll()
 }
 }

Slide 47

Slide 47 text

interface BanksRepository : Repository {
 fun getAll(): Observable>
 fun update(bank: Bank): Completable
 }

Slide 48

Slide 48 text

interface BanksRepository : Repository {
 fun getAll(): Observable>
 fun update(bank: Bank): Completable
 }

Slide 49

Slide 49 text

interface BanksRepository : Repository {
 fun getAll(): Observable>
 fun update(bank: Bank): Completable
 }

Slide 50

Slide 50 text

Data • Repository੄ पઁ ҳഅ

Slide 51

Slide 51 text

Data • Repository੄ पઁ ҳഅ • Data Source ੄ઓࢿ

Slide 52

Slide 52 text

Data • Repository੄ पઁ ҳഅ • Data Source ੄ઓࢿ • Android ੄ઓࢿ

Slide 53

Slide 53 text

override fun getAll(): Observable> {
 return Observable.fromCallable {
 with(Realm.getDefaultInstance()) {
 use {
 where(BankModel::class.java).findAll().map { 
 BankEntityMapper.fromRealmObject(it) 
 }
 } 
 }
 }
 }

Slide 54

Slide 54 text

override fun getAll(): Observable> {
 return Observable.fromCallable {
 with(Realm.getDefaultInstance()) {
 use {
 where(BankModel::class.java).findAll().map { 
 BankEntityMapper.fromRealmObject(it) 
 }
 } 
 }
 }
 }

Slide 55

Slide 55 text

override fun getAll(): Observable> {
 return Observable.fromCallable {
 with(Realm.getDefaultInstance()) {
 use {
 where(BankModel::class.java).findAll().map { 
 BankEntityMapper.fromRealmObject(it) 
 }
 } 
 }
 }
 }

Slide 56

Slide 56 text

open class Bank : RealmObject() {
 @PrimaryKey open lateinit var id: String
 open lateinit var name: String
 open lateinit var imageUrl: String
 }

Slide 57

Slide 57 text

override fun getAll(): Observable> {
 return Observable.fromCallable {
 with(Realm.getDefaultInstance()) {
 use {
 where(BankModel::class.java).findAll().map { 
 BankEntityMapper.fromRealmObject(it)
 }
 } 
 }
 }
 }

Slide 58

Slide 58 text

object BankEntityMapper : EntityMapper {
 override fun fromRealmObject(obj: BankModel): Bank {
 return Bank(
 obj.id,
 obj.name,
 obj.imageUrl
 )
 }
 
 override fun toRealmObject(entity: Bank): BankModel {
 return BankModel().apply {
 id = entity.id
 name = entity.name
 imageUrl = entity.imageUrl
 }
 }
 }

Slide 59

Slide 59 text

object BankEntityMapper : EntityMapper {
 override fun fromRealmObject(obj: BankModel): Bank {
 return Bank(
 obj.id,
 obj.name,
 obj.imageUrl
 )
 }
 
 override fun toRealmObject(entity: Bank): BankModel {
 return BankModel().apply {
 id = entity.id
 name = entity.name
 imageUrl = entity.imageUrl
 }
 }
 }

Slide 60

Slide 60 text

class BanksNetworkRepository(private val context: Context) : BanksRepository {
 override fun getAll(): Observable> {
 return context.retrofit.banksApi.getSupportedBanks()
 .map(GetSupportedBanksResponse::banks)
 .map { it.map { BankEntityMapper.fromNetworkResponseModel(it) } }
 }
 }

Slide 61

Slide 61 text

data class Bank(
 @SerializedName("id") var id: String? = null,
 @SerializedName("name") var name: String? = null,
 @SerializedName("image_url") var imageUrl: String? = null
 ) : Response

Slide 62

Slide 62 text

Presentation • UI ۨ߰੄ ୊ܻ

Slide 63

Slide 63 text

Presentation • UI ۨ߰੄ ୊ܻ • Android ੄ઓࢿ

Slide 64

Slide 64 text

Presentation • UI ۨ߰੄ ୊ܻ • Android ੄ઓࢿ • View৬ Presenter

Slide 65

Slide 65 text

class OrganizationFragment : BaseFragment(), OrganizationView {
 override val presenter: OrganizationPresenter by lazy {
 OrganizationPresenter(this, act)
 }
 
 override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? {
 return inflater!!.inflate(R.layout.fragment_signup_organization, container, false)
 }
 
 override fun onViewCreated(view: View?, saved: Bundle?) {
 super.onViewCreated(view, saved)
 presenter.onViewCreated()
 nextBtn.onClick { presenter.onNextButtonClicked() }
 }
 }

Slide 66

Slide 66 text

class OrganizationFragment : BaseFragment(), OrganizationView {
 override val presenter: OrganizationPresenter by lazy {
 OrganizationPresenter(this, act)
 }
 
 override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? {
 return inflater!!.inflate(R.layout.fragment_signup_organization, container, false)
 }
 
 override fun onViewCreated(view: View?, saved: Bundle?) {
 super.onViewCreated(view, saved)
 presenter.onViewCreated()
 nextBtn.onClick { presenter.onNextButtonClicked() }
 }
 }

Slide 67

Slide 67 text

class OrganizationFragment : BaseFragment(), OrganizationView {
 override val presenter: OrganizationPresenter by lazy {
 OrganizationPresenter(this, act)
 }
 
 override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? {
 return inflater!!.inflate(R.layout.fragment_signup_organization, container, false)
 }
 
 override fun onViewCreated(view: View?, saved: Bundle?) {
 super.onViewCreated(view, saved)
 presenter.onViewCreated()
 nextBtn.onClick { presenter.onNextButtonClicked() }
 }
 }

Slide 68

Slide 68 text

class OrganizationFragment : BaseFragment(), OrganizationView {
 override val presenter: OrganizationPresenter by lazy {
 OrganizationPresenter(this, act)
 }
 
 override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? {
 return inflater!!.inflate(R.layout.fragment_signup_organization, container, false)
 }
 
 override fun onViewCreated(view: View?, saved: Bundle?) {
 super.onViewCreated(view, saved)
 presenter.onViewCreated()
 nextBtn.onClick { presenter.onNextButtonClicked() }
 }
 }

Slide 69

Slide 69 text

class OrganizationFragment : BaseFragment(), OrganizationView {
 override val presenter: OrganizationPresenter by lazy {
 OrganizationPresenter(this, act)
 }
 
 override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, saved: Bundle?): View? {
 return inflater!!.inflate(R.layout.fragment_signup_organization, container, false)
 }
 
 override fun onViewCreated(view: View?, saved: Bundle?) {
 super.onViewCreated(view, saved)
 presenter.onViewCreated()
 nextBtn.onClick { presenter.onNextButtonClicked() }
 }
 }

Slide 70

Slide 70 text

class OrganizationPresenter(
 view: OrganizationView,
 context: Context,
 private val getBanks: GetBanks = GetBanks(BanksNetworkRepository(context))
 ) : Presenter(view) { override fun onDestroy() {
 getBanks.unsubscribe()
 } 
 fun onViewCreated() {
 getBanks.execute(object : DefaultSubscriber>() {
 override fun onNext(t: List) {
 view.showBanks(t)
 }
 })
 }
 
 fun onNextButtonClicked() {
 with(view.getSelectedOrganizations()) {
 if (isNotEmpty()) {
 view.moveToAddCertificatePage(this)
 }
 }
 }
 }

Slide 71

Slide 71 text

class OrganizationPresenter(
 view: OrganizationView,
 context: Context,
 private val getBanks: GetBanks = GetBanks(BanksNetworkRepository(context))
 ) : Presenter(view) { override fun onDestroy() {
 getBanks.unsubscribe()
 } 
 fun onViewCreated() {
 getBanks.execute(object : DefaultSubscriber>() {
 override fun onNext(t: List) {
 view.showBanks(t)
 }
 })
 }
 
 fun onNextButtonClicked() {
 with(view.getSelectedOrganizations()) {
 if (isNotEmpty()) {
 view.moveToAddCertificatePage(this)
 }
 }
 }
 }

Slide 72

Slide 72 text

class OrganizationPresenter(
 view: OrganizationView,
 context: Context,
 private val getBanks: GetBanks = GetBanks(BanksNetworkRepository(context))
 ) : Presenter(view) { override fun onDestroy() {
 getBanks.unsubscribe()
 } 
 fun onViewCreated() {
 getBanks.execute(object : DefaultSubscriber>() {
 override fun onNext(t: List) {
 view.showBanks(t)
 }
 })
 }
 
 fun onNextButtonClicked() {
 with(view.getSelectedOrganizations()) {
 if (isNotEmpty()) {
 view.moveToAddCertificatePage(this)
 }
 }
 }
 }

Slide 73

Slide 73 text

class OrganizationPresenter(
 view: OrganizationView,
 context: Context,
 private val getBanks: GetBanks = GetBanks(BanksNetworkRepository(context))
 ) : Presenter(view) { override fun onDestroy() {
 getBanks.unsubscribe()
 } 
 fun onViewCreated() {
 getBanks.execute(object : DefaultSubscriber>() {
 override fun onNext(t: List) {
 view.showBanks(t)
 }
 })
 }
 
 fun onNextButtonClicked() {
 with(view.getSelectedOrganizations()) {
 if (isNotEmpty()) {
 view.moveToAddCertificatePage(this)
 }
 }
 }
 }

Slide 74

Slide 74 text

class OrganizationPresenter(
 view: OrganizationView,
 context: Context,
 private val getBanks: GetBanks = GetBanks(BanksNetworkRepository(context))
 ) : Presenter(view) { override fun onDestroy() {
 getBanks.unsubscribe()
 } 
 fun onViewCreated() {
 getBanks.execute(object : DefaultSubscriber>() {
 override fun onNext(t: List) {
 view.showBanks(t)
 }
 })
 }
 
 fun onNextButtonClicked() {
 with(view.getSelectedOrganizations()) {
 if (isNotEmpty()) {
 view.moveToAddCertificatePage(this)
 }
 }
 }
 }

Slide 75

Slide 75 text

data class Amount(
 var amount: Long,
 var currency: Currency
 ) : Parcelable {
 
 companion object {
 @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
 override fun createFromParcel(source: Parcel): Amount = Amount(source)
 override fun newArray(size: Int): Array = arrayOfNulls(size)
 }
 }
 
 constructor(valueObject: AmountValueObject) : this(
 valueObject.amount,
 Currency.from(valueObject.currency)
 )
 
 constructor(source: Parcel) : this(
 source.readLong(),
 Currency.from(source.readString())
 )
 
 override fun describeContents() = 0
 
 override fun writeToParcel(dest: Parcel?, flags: Int) {
 dest?.writeLong(amount)
 dest?.writeString(currency.code)
 }
 }

Slide 76

Slide 76 text

ۨ੉פझ౟ীࢲח • જ਷ ௏٘੄ ҳઑо જ਷ ઁಿਵ۽ ੉য૓׮Ҋ ޺ӝী ੉۞ೠ ইఃఫ୊ܳ ੸ਊೞ৓णפ׮.

Slide 77

Slide 77 text

ۨ੉פझ౟ীࢲח • જ਷ ௏٘੄ ҳઑо જ਷ ઁಿਵ۽ ੉য૓׮Ҋ ޺ӝী ੉۞ೠ ইఃఫ୊ܳ ੸ਊೞ৓णפ׮. • ইఃఫ୊ بੑ੄ ૓ੑ ੢߷ࠁ׮ ߸ചী ੸਽ೡ ࣻ ੓਺ীࢲ য়ח ਬোೣ੉ ؊ ч૑׮Ҋ ࢤпೞৈ ੉۞ೠ ইఃఫ୊ܳ ੸ਊ ೞ৓णפ׮.

Slide 78

Slide 78 text

ߛ௼࢟۞٘ܳ ݅٘ח ۨ੉פझ౟ীࢲ ੷൞ ৬ ೣԋೡ উ٘۽੉٘ ূ૑פয ٜ࠙ਸ ଺ Ҋ ੓णפ׮. Kotlin, RxJava, Retrofit, Glide, Realm

Slide 79

Slide 79 text

Questions github: sunghyunzz email: [email protected]