Slide 1

Slide 1 text

Android Clean Architecture BAU Mobile Application Fair
 GDG
 27th April 2018 Mohammed Touban

Slide 2

Slide 2 text

ideatolife.me

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

View Activity Web Service Activity Lifecycle System Services User Input Dependency Injection Repository Storage Presenter View EditText EditText Button

Slide 6

Slide 6 text

Clean Architecture

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

“Elegance in design… is a lack of unnecessary complexity”

Slide 14

Slide 14 text

Clean Architecture Separation of Concerns Build layered applications with clear separation of responsibilities including view, presentation, and domain logic. State Synchronization Synchronize data across screen state, session state, and server state. Testability Enables pure unit tests to test presentation logic and domain models in isolation. Integration and UI tests for components that interact with the framework and/or display.

Slide 15

Slide 15 text

visual representation of the Clean Architecture. All credit for this images goes to Uncle Bob

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

“Those who do not remember the past are condemned to repeat it.” — George Santayana

Slide 20

Slide 20 text

Clean Architecture Separation of Concerns Build layered applications with clear separation of responsibilities including view, presentation, and domain logic. State Synchronization Synchronize data across screen state, session state, and server state. Testability Enables pure unit tests to test presentation logic and domain models in isolation. Integration and UI tests for components that interact with the framework and/or display.

Slide 21

Slide 21 text

Separation of Concerns • • • View layer Presentation model Domain model

Slide 22

Slide 22 text

Clean Architecture Separation of Concerns Build layered applications with clear separation of responsibilities including view, presentation, and domain logic. State Synchronization Synchronize data across screen state, session state, and server state. Testability Enables pure unit tests to test presentation logic and domain models in isolation. Integration and UI tests for components that interact with the framework and/or display.

Slide 23

Slide 23 text

State Synchronization • • • Screen state Session state Server state

Slide 24

Slide 24 text

State Synchronization • • • Screen state Session state Server state

Slide 25

Slide 25 text

Clean Architecture Separation of Concerns Build layered applications with clear separation of responsibilities including view, presentation, and domain logic. State Synchronization Synchronize data across screen state, session state, and server state. Testability Enables pure unit tests to test presentation logic and domain models in isolation. Integration and UI tests for components that interact with the framework and/or display.

Slide 26

Slide 26 text

T estability • • • Android framework dependencies encapsulated in view layer Pure JVM unit tests for presenters and domain models Espresso, Robolectric, or UI Automator tests for view layer

Slide 27

Slide 27 text

Test Pyramid - mike cohen book in 2009 “succeeding with agile”

Slide 28

Slide 28 text

Android Test Pyramid

Slide 29

Slide 29 text

Integration Tests

Slide 30

Slide 30 text

Clean Architecture Separation of Concerns Build layered applications with clear separation of responsibilities including view, presentation, and domain logic. State Synchronization Synchronize data across screen state, session state, and server state. Testability Enables pure unit tests to test presentation logic and domain models in isolation. Integration and UI tests for components that interact with the framework and/or display.

Slide 31

Slide 31 text

GUI Architectures

Slide 32

Slide 32 text

GUI Architectures • • • • • • MVC MVP MVVM MVI MV… …

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

Supervising Controller (MVC) • • • • Smalltalk-80 One way flow of information Controller handles user input and UI events View observes changes in domain model State Sync: Data Binding Controller Model onLoginButtonClick() setUser() View View.onClick()

Slide 35

Slide 35 text

activity_login.xml

Slide 36

Slide 36 text

Passive View (MVP) • • • • Also called Humble View No dependency between view and domain model Bi-directional flow of information View replaced by fake in unit tests State Sync: Flow Synchronization View View.onClick() showUser() Presenter onLoginButtonClick() Model getUser() View showUser()

Slide 37

Slide 37 text

class LoginPresenter(private val view: LoginView) { fun onLoginButtonClick(email: String?, password: String?) { if (email != null && password != null) { val user = Model.getUser(email, password) if (user != null) { view.showUser(user) } else { view.showError(“Invalid credentials") } } } } LoginPresenter.kt

Slide 38

Slide 38 text

Presentation Model (MVVM) • • • View forwards user input to presentation model Presentation model mutates state using domain model View observes changes in the presentation model State Sync: Observer Pattern ViewModel Model onLoginButtonClick() getUser() View View.onClick() onUpdateUser( ) updateUser()

Slide 39

Slide 39 text

Supervising Controller (MVC) Passive View (MVP ) Presentation Model (MVVM ) Flow of Information One way Two way Two way Model <> View Dependency Yes No No Sync Logic View Presenter View State Synchronization Data binding Flow synchronization Observer pattern GUI Architectures

Slide 40

Slide 40 text

• • • • Passive view migrates all sync logic into presentation layer MVC and MVVM require sync logic in the view layer For simpler apps, MVC or MVVM may be sufficient MVP tests are more verbose and require a fake view Humble View is the real MVP

Slide 41

Slide 41 text

Android Passive View Presenter Activity Presentation Logic Model Domain Logic User Interface User Input Lifecycle Events System Services View

Slide 42

Slide 42 text

Android Passive View Presenter Activity Presentation Logic Model Domain Logic View User Interface User Input Lifecycle Events System Services View

Slide 43

Slide 43 text

Android Passive View Presenter Activity Presentation Logic Controller Model Domain Logic View User Interface User Input Lifecycle Events System Services

Slide 44

Slide 44 text

Android Passive View Presenter Activity Presentation Logic Controller Model Domain Logic View User Interface User Input Lifecycle Events System Services

Slide 45

Slide 45 text

Presenter Fake Controller Presentation Logic Controller Model Domain Logic Stub Implementation Passive View: Unit Tests Presenter Test Test Cases Model Test Test Cases

Slide 46

Slide 46 text

Presenter Activity Presentation Logic Controller Model Domain Logic View User Interface User Input Lifecycle Events System Services Passive View: UI Tests Activity Test Test Cases

Slide 47

Slide 47 text

class LoginActivity : AppCompatActivity(), LoginView { lateinit var presenter: LoginPresenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) presenter = LoginPresenter(this) login_button.setOnClickListener({ presenter.onLoginButtonClick(login_view.email, login_view.password) }) } // ... } LoginActivity.kt

Slide 48

Slide 48 text

interface LoginView { fun showUser(user: User) fun showError(error: String) } LoginView.kt

Slide 49

Slide 49 text

activity_login.xml

Slide 50

Slide 50 text

class LoginView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) { var email: String? = null get() = login_email.text.toString() var password: String? = null get() = login_password.text.toString() var error: String? = null set(value) { login_error.text = value } init { (getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater).inflate(R.layout.view_login, this, true) } } LoginView.kt

Slide 51

Slide 51 text

view_login.xml

Slide 52

Slide 52 text

class LoginPresenter(private val view: LoginView) { fun onLoginButtonClick(email: String?, password: String?) { if (email != null && password != null) { // … get user from REST API call if (user != null) { view.showUser(user) } else { view.showError(“Invalid credentials") } } } } LoginPresenter.kt

Slide 53

Slide 53 text

class LoginActivity : AppCompatActivity(), LoginView{ // ... override fun showUser(user: User) { login_view.visibility = View.GONE user_view.visibility = View.VISIBLE user_view.user = user } override fun showError(error: String) { login_view.error = error } } LoginActivity.kt

Slide 54

Slide 54 text

class UserView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) { var user: User? = null set(value) { user_name.text = value?.name user_email.text = value?.email } init { (getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater).inflate(R.layout.view_user, this, true); } } UserView.kt

Slide 55

Slide 55 text

view_user.xml

Slide 56

Slide 56 text

Android-y challenges • • • • • Foreground/background events Configuration change Presenter scope Long-running operations Data persistence

Slide 57

Slide 57 text

Android Architecture Components Android Architecture Components

Slide 58

Slide 58 text

Android Architecture Components • • • • • Lifecycle ViewModel LiveData Room Paging Library

Slide 59

Slide 59 text

Controller LifecycleActivity Web Service Activity Lifecycle System Services User Input Dependency Injection Repository Model Model Storage Presenter View EditText EditText Button

Slide 60

Slide 60 text

Controller LifecycleActivity Web Service Activity Lifecycle System Services User Input Dependency Injection Repository Model LiveData Room Storage Presenter ViewModel View EditText EditText Button

Slide 61

Slide 61 text

Some thoughts for the future… • • • • • Community-driven architecture Mix and match components Fragments? Lifecycle aware libraries We are all in this together

Slide 62

Slide 62 text

Android Clean Architecture BAU Mobile Application Fair
 GDG
 27th April 2018 Mohammed Touban