Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Droidcon 2020. Why We Need Clean Architecture

Igor Wojda
November 17, 2020

Droidcon 2020. Why We Need Clean Architecture

"You can’t anticipate everything that’s going to change in the software but you can try to build in enough flexibility that it’s likely to adapt to most changes"

Application architecture defines the way we develop, modularise, test, scale, and maintain our application. A good architecture allows us to deal with never-ending stream requirements by providing enough flexibility and solid protection against regression.

Igor will introduce the concept of Clean Architecture in the context of Android Development, discuss it's benefits, helps you understand how Clean Architecture fits into modern Android application development.

Igor Wojda

November 17, 2020
Tweet

More Decks by Igor Wojda

Other Decks in Technology

Transcript

  1. “ 7 We should think about our applications as group

    of use cases that describe the intent of the application group of plugins that give those uses cases access to outside word. Uncle Bob and
  2. WHY CLEAN ARCHITECTURE? ◦ Independent of libraries & frameworks ◦

    Independent of UI ◦ Independent of data sources 8 Business Logic UI Libraries & Frameworks Data sources
  3. UI Data source ViewModel Repository Use case Entity Presentation Layer

    Domain Layer Data Layer LAYERS 13 Data Flow Dependency rule Dependency rule Data Flow
  4. ViewModel View Use case Entity Repository Network Data Source Disk

    Data Source 14 LAYERS Presentation Layer Domain Layer Data Layer
  5. DEPENDENCY INVERSION 15 UseCase Repository • Define interface • Class

    Repository implements this interface • Class Repository is injected into UseCase class UseCase Repository
  6. 16 LAYERS ViewModel View Use case Entity Repository Network Data

    Source Disk Data Source Dependencies Presentation Layer Domain Layer Data Layer
  7. DATA MODEL 18 data class User( val id: String, val

    firstName: String, val lastName: String, val birthDate: String )
  8. ADD GSON ANNOTATIONS 19 data class User( @PrimaryKey("id") val id:

    String, @SerializedName("first_name") val firstName: String, @SerializedName("last_name") val lastName: String, @SerializedName("birth_date") val birthDate: String )
  9. ADD ROOM ANNOTATIONS 20 @Entity(tableName = "users") data class User(

    @PrimaryKey("id") @SerializedName("id") val id: String, @ColumnInfo(name = "first_name") @SerializedName("first_name") val firstName: String, @ColumnInfo(name = "last_name") @SerializedName("last_name") val lastName: String, @ColumnInfo(name = "birth_date") @SerializedName("birth_date") val birthDate: String )
  10. ADD GETTERS 21 @Entity(tableName = "users") data class User( @PrimaryKey("id")

    @SerializedName("id") val id: String, @ColumnInfo(name = "first_name") @SerializedName("first_name") val firstName: String, @ColumnInfo(name = "last_name") @SerializedName("last_name") val lastName: String, @ColumnInfo(name = "birth_date") @SerializedName("birth_date") val birthDate: String ) { val fullName = "$firstName $lastName” val isAdult = DateTime.parse(dateOfBirth) .plusYears(18) .isAfter(DateTime.now()) }
  11. THE GOOD MODEL 22 @Entity(tableName = "users") data class User(

    @PrimaryKey("id") @SerializedName("id") val id: String, @ColumnInfo(name = "first_name") @SerializedName("first_name") val firstName: String, @ColumnInfo(name = "last_name") @SerializedName("last_name") val lastName: String, @ColumnInfo(name = "birth_date") @SerializedName("birth_date") val birthDate: String ) { val fullName = "$firstName $lastName” val isAdult = DateTime.parse(dateOfBirth) .plusYears(18) .isAfter(DateTime.now()) }
  12. USER NETWORK MODEL 25 Data Layer data class UserNetworkModel( @SerializedName("id")

    val id: String, @SerializedName("first_name") val firstName: String, @SerializedName("last_name") val lastName: String, @SerializedName("birth_date") val birthDate: String )
  13. USER ROOM MODEL 26 Data Layer @Entity(tableName = "users") data

    class UserRoomModel( @PrimaryKey("id") val id: String, @ColumnInfo(name = "first_name") val firstName: String, @ColumnInfo(name = "last_name") val lastName: String, @SerializedName("birth_date") val birthDate: String )
  14. USER MODEL 27 data class UserModel( val id: String, val

    firstName: String, val lastName: String, val birthDate: DateTime ) Domain Layer
  15. FIRST NAME REPRESENTATION 29 UserPresentationModel UserModel UserNetworkModel UserRoomModel UserNetworkMapper UserRoomMapper

    UserPresentationMapper Presentation Layer Domain Layer Data Layer “Igor" “Igor" “Igor" “Igor"
  16. DATE & TIME REPRESENTATION 30 UserPresentationModel UserModel UserNetworkModel UserRoomModel UserNetworkMapper

    UserRoomMapper UserPresentationMapper Presentation Layer Domain Layer Data Layer "20201107T170000Z" 1604407243 "11th Nov 2020” DateTime
  17. MAPPER AS INFERFACE 35 interface Mapper<in FROM, out TO> {

    fun map(from: FROM): TO } class UserNetworkModelMapper : Mapper<UserNetworkModel, UserModel> { override fun map(UserNetworkModel: UserNetworkModel) = UserModel( id = UserNetworkModel.id, firstName = UserNetworkModel.firstName, lastName = UserNetworkModel.lastName, birthDate = DateTime.parse(UserNetworkModel.birthDate) ) }
  18. MAPPER AS INFERFACE 36 interface Mapper<in FROM, out TO> {

    fun map(from: FROM): TO } class UserNetworkModelMapper : Mapper<UserNetworkModel, UserModel> { override fun map(UserNetworkModel: UserNetworkModel) = UserModel( id = UserNetworkModel.id, firstName = UserNetworkModel.firstName, lastName = UserNetworkModel.lastName, birthDate = DateTime.parse(UserNetworkModel.birthDate) ) }
  19. MAPPER AS INFERFACE 37 interface Mapper<in FROM, out TO> {

    fun map(from: FROM): TO } class UserNetworkModelMapper : Mapper<UserNetworkModel, UserModel> { override fun map(UserNetworkModel: UserNetworkModel) = UserModel( id = UserNetworkModel.id, firstName = UserNetworkModel.firstName, lastName = UserNetworkModel.lastName, birthDate = DateTime.parse(UserNetworkModel.birthDate) ) }
  20. MAPPER AS EXTENSION 38 val usedModel = userNetworkModel.toDomainModel() fun UserNetworkModel.toDomainModel()

    = UserModel( id = this.id, firstName = this.firstName, lastName = this.lastName, birthDate = DateTime.parse(UserNetworkModel.birthDate) )
  21. ViewModel View Use case Entity Repository Network Data Source Disk

    Data Source 39 VIEW MODEL IMPLEMENTATION Presentation Layer Domain Layer Data Layer Abstractions
  22. ViewModel View Use case Entity Repository Network Data Source Disk

    Data Source 40 VIEW MODEL IMPLEMENTATION Presentation Layer Domain Layer Data Layer Abstractions
  23. VIEW MODEL IMPLEMENTATION 43 class UsersViewModel(val getUsersUseCase: GetUsersUseCase) : ViewModel

    {
 
 fun getUsers() { viewModelScope.launch {
 getUsersUseCase.execute().also { }
 }
 }
  24. VIEW MODEL IMPLEMENTATION 44 class UsersViewModel(val getUsersUseCase: GetUsersUseCase) : ViewModel

    {
 
 fun getUsers() { viewModelScope.launch {
 getUsersUseCase.execute().also { result -> when (result) { is Success -> { // do something } is Error -> { // do something } } }
 }
 }
  25. VIEW MODEL IMPLEMENTATION 45 class UsersViewModel(val getUsersUseCase: GetUsersUseCase) : ViewModel

    {
 
 fun getUsers() { viewModelScope.launch {
 getUsersUseCase.execute().also { result -> when (result) { is Success -> { // do something } is Error -> { // do something } } }
 }
 }
  26. Presentation Layer Domain Layer Data Layer ViewModel View Use case

    Entity Repository Network Data Source Disk Data Source 46 USE CASE IMPLEMENTATION Abstractions
  27. USE CASE IMPLEMENTATION 49 class GetAdultUsersUseCase(val UsersRepository: UsersRepository) { suspend

    fun execute(): List<User> = UserRepository.getUsers() .filter { it.adult } }
  28. USE CASE IMPLEMENTATION 50 class GetAdultUsersUseCase(val UsersRepository: UsersRepository) { suspend

    fun execute(): List<User> = UserRepository.getUsers() .filter { it.adult } }
  29. Presentation Layer Domain Layer Data Layer ViewModel View Use case

    Entity Repository Network Data Source Disk Data Source 51 USE CASE IMPLEMENTATION Abstractions
  30. ENTITY IMPLEMENTATION 52 data class UserModel( val id: String, val

    firstName: String, val lastName: String, val birthDate: DateTime )
  31. Presentation Layer Domain Layer Data Layer ViewModel View Use case

    Entity Repository Network Data Source Disk Data Source 53 USE CASE IMPLEMENTATION Abstractions
  32. Presentation Layer Domain Layer Data Layer ViewModel View Use case

    Entity Repository Network Data Source Disk Data Source 55 REPOSITORY IMPLEMENTATION Abstractions
  33. class UsersRepositoryImpl( private val usersRetrofitService: UsersRetrofitService ) : UsersRepository {

    override suspend fun getUsers() = usersRetrofitService.getUsersAsync() } REPOSITORY IMPLEMENTATION 57
  34. class UsersRepositoryImpl( private val usersRetrofitService: UsersRetrofitService ) : UsersRepository {

    override suspend fun getUsers() = usersRetrofitService.getUsersAsync() .map { it.toDomainModel() } } REPOSITORY IMPLEMENTATION 58
  35. class UsersRepositoryImpl( private val usersRetrofitService: UsersRetrofitService ) : UsersRepository {

    override suspend fun getUsers() = usersRetrofitService.getUsersAsync() .map { it.toDomainModel() } } REPOSITORY IMPLEMENTATION 59
  36. 62 MODULARITY Presentation Layer Domain Layer Data Layer ViewModel Business

    Logic View GridView Recycler View TabLayout BottomBar User Repository Tracking Repository SQLite Volley Firebase Retrofit MixPanel Room
  37. 64 TESTING Presentation Layer Domain Layer Data Layer ViewModel Use

    Case View Repository Entity Mapper Mock Room Data Source Mock Network Data Source Mock Repository Mock ViewModel Mock Mapper Mock Mapper Mock Mapper Mock Entity Entity Entity Use Case Mock Mapper Mock
  38. 65 PROS AND CONS • Great for long living projects

    • Great for big teams • Maintainability • Scalability • Parallel teams • Maintainable • Testable Cons Pros • Boilerplate code • Not suitable for all projects • Multiple ways to implement
  39. 66 REFERENCES Articles • The Clean Architecture https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean- architecture.html •

    Multiple ways of defining Clean Architecture layers https://proandroiddev.com/ multiple-ways-of-defining-clean-architecture-layers-bbb70afa5d4a Android projects • https://github.com/igorwojda/android-showcase • https://github.com/android10/Android-CleanArchitecture-Kotlin • https://github.com/bufferapp/android-clean-architecture-boilerplate • https://github.com/bufferapp/clean-architecture-components-boilerplate Talks • https://youtu.be/Nsjsiz2A9mg • https://youtu.be/3Mq5newPdck • https://youtu.be/-sEmrJOk1uI • https://youtu.be/su34EYeQ90E
  40. 68 Special thanks to SlidesCarnival who made and released these

    awesome presentation template for free. CREDITS