Archetype - modern
Android app
architecture
Stepan Goncharov @ AppsConf 2017
Video produc+on pla/orm
Over 16k Videos Created with 2300 Brands
What is mobile app
Architecture?
Unified set of rules that allow teams
to effectively solve problems and
implement business requirements
Architecture
Source code problems
• Code organisation rules
• Better code understanding
• Flexibility
• Extendability
• Testability
Problems every architecture need to solve
• Navigation
• Dialogs
• Network requests
• Restore state
• Services
• Lifecycle
• Activity result
• and more
Tools
• Android Studio
• Kotlin
• rxJava 2
• Dagger 2
• DataBindings
• Navi (arch.lifecycle)
• Custom BaseViewModel (arch.ViewModel)
Mobius 2017 story
Hype driven development?
Not anymore ;)
17 May 2017
Google I/O
Google endorses
• Kotlin - supported in Android Studio
• Dagger 2 - recommended DI tool for Android apps
• rxJava 2 - supported by Room ORM
• Arch components announced
• MVVM recommended
Archetype vs Architecture components
UI controller
(Activity/Fragment)
ViewModel
Repository
Data Source
XML Layout

View
More abstractions!
Archetype vs Architecture components
UI Container
(Activity/Fragment)
View Model
Repository
Middleware
(Context)
Binding
Use case

(Interactor)
Action
XML Layout

View
Building blocks
• View’s - DataBinding’s layout
• View-Property Binding’s
• ViewModel’s
• UseCase’s aka Interactor
• Repositories
• Action’s
MVVM
View
• Android XML layout
<layout …>
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout …>
<TextView …
android:text="@{user.firstName}"/>
<TextView …
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>
Binding
• Generated from XML
//SomeFragment.kt
override fun onCreateView(
inflater: LayoutInflater?,
container: ViewGroup?,
state: Bundle?): View {

super.onCreateView(inflater, container, savedInstanceState)

binding = DataBindingUtil.inflate(inflater, layoutId, container, false)
…
}
ViewModel
• Transform data for View
• Fragment* set ViewModel in Binding
class LoginViewModel @Inject constructor(

viewModel: ViewModel,

loginState: LoginState,
resourceProvider: ResourceProvider,
loginUseCase: LoginUseCase

) :

ViewModel by viewModel,

LoginState by loginState,

ResourcesProvider by resourceProvider,

LoginComponent by loginUseCase {
…
}
UseCase
• Encapsulate Business logic
interface EnvironmentUseCase {

fun observeEnvironment(): Observable<EnvironmentModel>

}



class EnvironmentUseCaseImpl @Inject constructor(
val repo: EnvironmentRepo
) : EnvironmentUseCase {



override fun observeEnvironment()

= repo.observe().filterNonEmpty()



}
Repo
• Encapsulate Access to data
class FirebaseAuthModelRepo(

db: FirebaseDatabase

) : AuthModelRepo,
SingleValueRepo<AuthModel> by FirebaseSingleValueRepo(db, AUTH_MODEL)
interface AuthModelRepo
: SingleValueRepo<AuthModel> {

fun push(value: UserLoginModel) = LoginAction(value).asSingle()
}
Action
• Interact with Middleware
• Serializable - ID + Args
• Non-serialisable
Middleware
• DB
• Network
• Android SDK
• Libraries
• Sensors
• etc.
Data flow
BindingView
Data flow
BindingView
Middleware
(Context)
Action
Data flow
View ModelBindingView
Middleware
(Context)
Action
Data flow
View ModelBindingView
Middleware
(Context)
Action
Middleware
(Context)
Action
Data flow
View ModelBinding
Use case

(Interactor)
View
Middleware
(Context)
Action
Middleware
(Context)
Action
Data flow
View ModelBinding
Use case

(Interactor)
View
Middleware
(Context)
Action
Middleware
(Context)
Action
Middleware
(Context)
Action
Data flow
View Model Repository
Middleware
(Context)
Binding
Use case

(Interactor)
Action
View
Middleware
(Context)
Action
Middleware
(Context)
Action
Middleware
(Context)
Action
Data flow
View Model Repository
Middleware
(Context)
Binding
Use case

(Interactor)
Action
View
Middleware
(Context)
Action
Middleware
(Context)
Action
Middleware
(Context)
Action
Composition
Repository
Use case

(Interactor)
Composition
Repository
Use case

(Interactor)
Repository
Composition
Repository
Use case

(Interactor)
Use case

(Interactor)
Repository
View Model
Use case

(Interactor)
Use case

(Interactor)
How does it help?
• Better code organisation - fixed amount of abstractions
• Smaller abstractions
• Easier testing
• Flexible 2-5 layers
• Extendable through Middleware
Back to our problems
Navigation
• Middleware - android.content.Context
class ShowEpisodeAction : IntentAction,
IntentMaker by Injector().intentMaker() {



override fun invoke(context: Context, args: Args): Completable

= startIntent<PlayerActivity>(context, args)



}
Dialogs
• Middleware - android.content.Context
class ShowEpisodeDialog(dialogMaker: DialogMaker) :
DialogMaker by dialogMaker {



override fun invoke(context: Context, args: Args): Completable

= showDialog<EpisodeDialog>(context, args)



}
State/data sharing
Service
State/data sharing
RepositoryService
Network
• Middleware - Retrofit
class GetEpisodesAction : ApiAction {



val episodesRepo by lazyInject { episodesRepo() }



override fun invoke(context: Api, args: Args): Completable = context.feed()

.map { it.channel.item.map(::transformResponse) }

.flatMap { episodesRepo.save(it.associateBy({ it.id }) { it }) }

.subscribeOn(io())

.toCompletable()



}
Call action from repo
interface EpisodesModelRepo : KeyValueRepo<Long, EpisodesModel> {

val action: ActionGetEpisodes


fun pull(): Completable = action.invoke()
}
Lifecycle events
class EpisodesViewModel(

naviComponent: NaviComponent

) :

ViewModel by ViewModelImpl(naviComponent = naviComponent) {



init {

observe(Event.RESUME)
.doOnNext { refresh() }

.bindSubscribe()

}



fun refresh() = …
}
Activity result
class EpisodesViewModel(

naviComponent: NaviComponent

) :

ViewModel by ViewModelImpl(naviComponent = naviComponent) {



init {

observe(Event.ACTIVITY_RESULT)
.doOnNext { handleActivityResutl(it) }

.bindSubscribe()

}



fun handleActivityResult() = …
}
Thanks!
Whats next
• Ask your question right now
• Get example https://github.com/stepango/Archetype
• Watch Mobius 2017 talk
Social links
• @stepango
• @nekdenis
• http://androiddev.apptractor.ru
• https://t.me/archetype_android
Q&A


What challenges you have in your project?


P.S. We are hiring!

Современная архитектура Android-приложений - Archetype / Степан Гончаров (90 Seconds)