SlideShare a Scribd company logo
Clean Architecture:
A Guided Tour
Yossi Segev
Tech Lead @ Kin Ecosystem
@yossisegev
yossisegev.com
Clean Architecture:
A Guided Tour
Yossi Segev
Tech Lead @ Kin Ecosystem
@yossisegev
yossisegev.com
Why are we obsessed
with architecture?
We want to be able to
REACT TO CHANGES
quickly & efficiently
Good architecture is
•Easy to maintain / expand upon
•Easy to test
•Easy to understand
Keep in mind
•Architecture is dynamic and ever-evolving
•There are always several solutions to every problem
•Every architecture decision is a trade-off
•There is no such thing as “the perfect
architecture”
Clean Architecture
The Layers Principle
Layer C
Layer B
Layer A
DependenciesFlow
<< Detailed
<< Generic
Movie Night app layers
Presentation
Data
Domain
DependenciesFlow
Movie Night app layers
Presentation
Data
Domain
•UI
•Presenters
•Dependency Injection
•Web API
•Storage (DB/In-Mem)
•Use cases
•Interfaces
•Domain Entities
DependenciesFlow
Modules as layers
*Not by dependency flow order
build.gradle
dependencies {
implementation project(':domain')
// ...
}
Movie Night app layers
Presentation
Data
Domain
•UI
•Presenters
•Dependency Injection
•Web API
•Storage (DB/In-Mem)
•Use cases
•Interfaces
•Domain Entities
DependenciesFlow
The Domain Layer
•The baseline of the application
•Describes what the application is / what it can do
•Generic code
• Domain Entities
• Use cases
• Interfaces
The Domain Layer
Domain Entities
• Simple data containers
• The basic building blocks of our application
• Acts as common language across the
application
MovieNight’s
Domain Entities
MovieEntity.kt
data class MovieEntity(
var id: Int = 0,
var title: String,
var overview: String? = null,
var voteCount: Int = 0
// ...
)
• Domain Entities
• Use cases
• Interfaces
The Domain Layer
Use Case
Encapsulates a single,
very specific task
to be performed
MovieNight’s Use Cases
UseCase.kt
abstract class UseCase<T>() {
abstract fun create(): Observable<T>
fun observable(): Observable<T> {
return create().compose(t)
}
}
UseCase.kt
abstract class UseCase<T>() {
abstract fun create(): Observable<T>
fun observable(): Observable<T> {
return create().compose(t)
}
}
UseCase.kt
abstract class UseCase<T>(t: ObservableTransformer) {
abstract fun create(): Observable<T>
fun observable(): Observable<T> {
return create().compose(t)
}
}
UseCase.kt
abstract class UseCase<T>(t: ObservableTransformer) {
abstract fun create(): Observable<T>
fun observable(): Observable<T> {
return create().compose(t)
}
}
UseCase.kt
abstract class UseCase<T>(t: ObservableTransformer) {
abstract fun create(): Observable<T>
fun observable(): Observable<T> {
return create().compose(t)
}
}
Using a UseCase
Using a UseCase
myUseCase
.observable()
.subscribe(
{ data ->
// Do something with the data
},
{ throwable ->
// Handle errors
}
)
Using a UseCase
myUseCase
.observable()
.subscribe(
{ data ->
// Do something with the data
},
{ throwable ->
// Handle errors
}
)
Using a UseCase
myUseCase
.observable()
.subscribe(
{ data ->
// Do something with the data
},
{ throwable ->
// Handle errors
}
)
Using a UseCase
myUseCase
.observable()
.subscribe(
{ data ->
// Do something with the data
},
{ throwable ->
// Handle errors
}
)
GetPopularMovies
UseCase
GetPopularMovies.kt
class GetPopularMovies(t: Transformer,
repo: MoviesRepo)
:UseCase<List<MovieEntity>>(t) {
override fun create(): Observable<List<MovieEntity>> {
return repo.getMovies()
}
}
GetPopularMovies.kt
class GetPopularMovies(t: Transformer,
repo: MoviesRepo)
:UseCase<List<MovieEntity>>(t) {
override fun create(): Observable<List<MovieEntity>> {
return repo.getMovies()
}
}
GetPopularMovies.kt
class GetPopularMovies(t: Transformer,
repo: MoviesRepo)
:UseCase<List<MovieEntity>>(t) {
override fun create(): Observable<List<MovieEntity>> {
return repo.getMovies()
}
}
MoviesRepo.kt
interface MoviesRepo {
fun getMovies(): Observable<List<MovieEntity>>
// . . .
}
• Domain Entities
• Use cases
• Interfaces
The Domain Layer
Interfaces
• Dictates the contract the upper layers must
follow
• Ensures the application core functionality will
hold true, regardless implementation details
changes
Moving up!
The Data Layer
Movie Night app layers
Data
Domain
•Web API
•Storage (DB/In-Mem)
•Domain Entities
•Interfaces
•Use cases
The Data Layer
•Caching mechanism
•Database
•Networking
•Mappers
The Data Layer
Encapsulates any knowledge regarding where the
application data is coming from and how it is stored
In-Mem
StorageAPIDB
Data Layer
Application
The Data Layer
•Caching mechanism
•Database
•Networking
•Mappers
Mappers
Maps Class A to Class B
Mapper.kt
abstract class Mapper<in E, T> {
abstract fun mapFrom(from: E): T
// ...
}
MyMapper.kt
class MyMapper: Mapper<MovieEntity, MovieData>() {
override fun mapFrom(from: MovieEntity): MovieData {
return MovieData(
id = from.id,
originalTitle = from.originalTitle,
releaseDate = from.releaseDate,
overview = from.overview
// ...
)
}
}
MyMapper.kt
class MyMapper: Mapper<MovieEntity, MovieData>() {
override fun mapFrom(from: MovieEntity): MovieData {
return MovieData(
id = from.id,
originalTitle = from.originalTitle,
releaseDate = from.releaseDate,
overview = from.overview
// ...
)
}
}
MyMapper.kt
class MyMapper: Mapper<MovieEntity, MovieData>() {
override fun mapFrom(from: MovieEntity): MovieData {
return MovieData(
id = from.id,
originalTitle = from.originalTitle,
releaseDate = from.releaseDate,
overview = from.overview
// ...
)
}
}
Why do we need Mappers?
import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey
import com.google.gson.annotations.SerializedName
@Entity(tableName = "movies")
data class MovieData(
@PrimaryKey
var id: Int = -1,
@SerializedName("original_title")
var originalTitle: String
// ...
)
MovieData.kt
Why do we need Mappers?
The Data layer contains details
we want to hide.
Data Layer
Mapper
DBApplication
Mapper
Data Layer
Mapper
DBApplication
Mapper
Data Layer
Mapper
DBApplication
Mapper
Data Layer
Mapper
DBApplication
Mapper
Data Layer
Mapper
DBApplication
Mapper
Summarising the
Data Layer
•Contains concrete implementations of data providers
•Act as a boundary, encapsulates data providers details
and hide them from the “outside world”.
Movie Night app layers
Presentation
Data
Domain
•UI
•Presenters
•Dependency Injection
•Web API
•Storage (DB/In-Mem)
•Use cases
•Interfaces
•Domain Entities
DependenciesFlow
The Presentation Layer
The Presentation Layer
Connects all the different pieces into a
single, functioning unit that is the
application
The Presentation Layer
•Activities, Fragments
•View States
•Presenters
•Mappers
•Dependency Injection
Presentation Layer
Architecture
Fragment
ViewModel
LiveData
Use Case
Notify
Observes
Why ViewModel & LiveData?
Lifecycle awareness.
ViewModel 101
class MainActivity : AppCompatActivity() {
val vm = ViewModelProviders
.of(lifecycleOwner)
.get(MyViewModel::class.java)
}
ViewModel 101
class MainActivity : AppCompatActivity() {
// Creation
val vm = ViewModelProviders
.of(lifecycleOwner)
.get(MyViewModel::class.java)
}
ViewModel 101
class MainActivity : AppCompatActivity() {
// Creation
val vm = ViewModelProviders
.of(lifecycleOwner)
.get(MyViewModel::class.java)
}
ViewModel 101
class MainActivity : AppCompatActivity() {
// Creation
val vm = ViewModelProviders
.of(lifecycleOwner)
.get(MyViewModel::class.java)
}
ViewModel 101
class MyViewModel: ViewModel() {
// ...
override fun onCleared() {
super.onCleared()
// Clear resources etc...
}
}
LiveData 101
LiveData 101
// Creating
val liveData = MutableLiveData<String>()
// Observing
liveData.observe(lifecycleOwner, Observer { data ->
// do something with data…
})
// Updating value
liveData.value = "Hello world!"
LiveData 101
// Creating
val liveData = MutableLiveData<String>()
// Observing
liveData.observe(lifecycleOwner, Observer { data ->
// do something with data…
})
// Updating value
liveData.value = "Hello world!"
LiveData 101
// Creating
val liveData = MutableLiveData<String>()
// Observing
liveData.observe(lifecycleOwner, Observer { data ->
// do something with data…
})
// Updating value
liveData.value = "Hello world!"
Fragment
ViewModel
LiveData
Presentation Layer
Architecture
Use Case
Notify
Observes
The Presentation Layer
•Activities, Fragments
•View States
•Presenters
•Mappers
•Dependency Injection
ViewModel
LiveData
Presentation Layer
Architecture
Fragment
Observes
Use Case
Notify
ViewState
Represents the state of a view
PopularViewState.kt
data class PopularViewState(
var loading: Boolean = true,
var movies: List<MovieEntity>? = null
)
ViewModel
LiveData
Presentation Layer
Architecture
Fragment
Subscribes
Use Case
Notify
ViewModel
LiveData
Presentation Layer
Architecture
Fragment
Subscribes
Use Case
Notify
PopularViewModel
class PopularViewModel(val useCase: GetPopularMovies)
: BaseViewModel() {
var viewState: MutableLiveData<PopularViewState>
init {
viewState.value = PopularViewState()
}
PopularViewModel.kt
class PopularViewModel(val useCase: GetPopularMovies)
: BaseViewModel() {
var viewState: MutableLiveData<PopularViewState>
init {
viewState.value = PopularViewState()
}
PopularViewModel.kt
class PopularViewModel(val useCase: GetPopularMovies)
: BaseViewModel() {
var viewState: MutableLiveData<PopularViewState>
init {
// Sets the initial view state
viewState.value = PopularViewState()
}
// ...
PopularViewModel.kt
getPopularMovies
.observable()
.subscribe({ movies ->
val newState = viewState.value
.copy(loading = false,
movies = movies)
this.viewState.value = newState
}, { throwable ->
// Handle errors
}))
PopularViewModel.kt
getPopularMovies
.observable()
.subscribe({ movies ->
}, { throwable ->
// Handle errors
}))
PopularViewModel.kt
getPopularMovies
.observable()
.subscribe({ movies ->
val newState = viewState.value
.copy(loading = false,
movies = movies)
}, { throwable ->
// Handle errors
}))
PopularViewModel.kt
getPopularMovies
.observable()
.subscribe({ movies ->
val newState = viewState.value
.copy(loading = false,
movies = movies)
viewState.value = newState
}, { throwable ->
// Handle errors
}))
PopularViewModel.kt
PopularViewModel.kt
val disposable = getPopularMovies
.observable()
.subscribe( /* ... */ )
addDisposable(disposable)
ViewModel
LiveData
Presentation Layer
Architecture
Fragment
Subscribes
Use Case
Notify
ViewModel
LiveData
Presentation Layer
Architecture
Fragment
Subscribes
Use Case
Notify
PopularMoviesFragment.kt
val viewModel = ViewModelProviders
.of(this, factory)
.get(PopularViewModel::class.java)
// ...
viewModel.viewState
.observe(this, Observer { state ->
handleViewState(state)
})
PopularMoviesFragment.kt
val viewModel = ViewModelProviders
.of(this, factory)
.get(PopularViewModel::class.java)
// ...
viewModel.viewState
.observe(this, Observer { state ->
handleViewState(state)
})
PopularMoviesFragment.kt
fun handleViewState(state: PopularViewState) {
if (state.loading) {
// Show or hide the progressBar
}
state.movies?.let {
// Show movies
}
}
The Presentation Layer
•Activities, Fragments
•View States
•Presenters
•Mappers
•Dependency Injection
Dependency Injection
• Wires everything across the application
• Providing concrete implementations
Summary
Thank you
https://github.com/mrsegev/MovieNight

More Related Content

Advanced #6 clean architecture