SlideShare a Scribd company logo
1 of 163
Download to read offline
Unidirectional data flow
on Android
… using Kotlin
@CesarValiente@CesarValiente
Who is this guy?
Image Placeholder
César Valiente
Android Engineer @Microsoft
Android Google Developer Expert (GDE)
@CesarValiente@CesarValiente
Unidirectional?
A B
Unidirectional?
A B
Can we have more steps?
A
B
Z
C D E
…
Can we have more steps?
A
B
Z
C D E
…
Can we have more steps?
A
B
Z
C D E
…
Can we have more steps?
A
B
Z
C D E
…
Can we have more steps?
A
B
Z
C D E
…
Can we have more steps?
A
B
Z
C D E
…
Can we have more steps?
A
B
Z
C D E
…
Can we have more steps?
A
B
Z
C D E
…
History
Flux Redux
Flux and Redux
Flux and Redux
Architectures based in unidirectional data flows.
Flux and Redux
Architectures based in unidirectional data flows.
We start in A and we finish in A.
Flux and Redux
Architectures based in unidirectional data flows.
We start in A and we finish in A.
Global app state. State has everything the App needs.
Flux
View
Action
Dispatcher
Store
Flux
DISPATCHER STORE VIEW
ACTION
Flux
DISPATCHER STORE VIEW
ACTION
Flux
DISPATCHER STORE VIEW
ACTION
Flux
DISPATCHER STORE VIEW
ACTION
Flux
DISPATCHER STORE VIEW
ACTION
Flux
ACTION DISPATCHER STORE VIEW
ACTION
Flux
ACTION DISPATCHER STORE VIEW
ACTION
Redux
Actions
Reducers
Store
Middleware (optional).
Redux
REDUCER VIEW
ACTION
STORE
Redux
REDUCER VIEW
ACTION
STORE
Redux
REDUCER VIEW
ACTION
STORE
Redux
REDUCER VIEW
ACTION
STORE
Redux
ACTION REDUCER VIEW
ACTION
STORE
Redux
ACTION REDUCER VIEW
ACTION
STORE
Why?(are we following this approach)
We use a mix of both
We use a mix of both
We have actions that describe what we want to do.
We use a mix of both
We have actions that describe what we want to do.
We have store.
We use a mix of both
We have actions that describe what we want to do.
We have store.
We have dispatcher that dispatch actions and new states.
Subscribers are subscribed to it.
We use a mix of both
We have actions that describe what we want to do.
We have store.
We have dispatcher that dispatch actions and new states.
Subscribers are subscribed to it.
We have reducers. Given an action return a new state.
We use a mix of both
We have actions that describe what we want to do.
We have store.
We have dispatcher that dispatch actions and new states.
Subscribers are subscribed to it.
We have reducers. Given an action return a new state.
We have middleware that extends our model (persistence, network, etc.)
We use a mix of both
We have actions that describe what we want to do.
We have store.
We have dispatcher that dispatch actions and new states.
Subscribers are subscribed to it.
We have reducers. Given an action return a new state.
We have middleware that extends our model (persistence, network, etc.)
We have ControllerView (we are in Android though :-) ).
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER MIDDLEWARE
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER MIDDLEWARE
STORE
Action
State
User interaction
UI Data
Optional Action
Our approach
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER MIDDLEWARE
STORE
Action
State
User interaction
UI Data
Optional Action
Dependencies
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
MIDDLEWARE
MIDDLEWARE
STORE
Dependencies
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
MIDDLEWARE
MIDDLEWARE
STORE
ANDROID
NO FRAMEWORK DEPENDENCIES
Dependencies
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
MIDDLEWARE
MIDDLEWARE
STORE
ANDROID
NO FRAMEWORK
DEPENDENCIES
ANDROID
NO FRAMEWORK DEPENDENCIES
Dependencies
ACTION
REDUCER
VIEW CONTROLLER-VIEW
STATE DISPATCHER ACTION DISPATCHER
MIDDLEWARE
MIDDLEWARE
STORE
ANDROID
KUnidirectional
KUnidirectional
OSS App created to show this architecture
on Android.
KUnidirectional
OSS App created to show this architecture
on Android.
A simple item list.
KUnidirectional
OSS App created to show this architecture
on Android.
A simple item list.
Everything is unidirectional.
KUnidirectional
OSS App created to show this architecture
on Android.
A simple item list.
Everything is unidirectional.
No external libraries, just Kotlin + Android.
View
View
Are Activities and Fragments.
View
Are Activities and Fragments.
It receives data from the ControllerView and uses it to update it self.
View
Are Activities and Fragments.
It receives data from the ControllerView and uses it to update it self.
It communicates with the ControllerView through an interface.
View
Are Activities and Fragments.
It receives data from the ControllerView and uses it to update it self.
We instantiate rest of the components here.
It communicates with the ControllerView through an interface.
ControllerView
ControllerView
“Man in the middle” between UI and everything else.
ControllerView
“Man in the middle” between UI and everything else.
Powered presenter.
ControllerView
“Man in the middle” between UI and everything else.
Powered presenter.
1 Activity/1Fragment -> 1 Controller-View.
ControllerView
“Man in the middle” between UI and everything else.
Powered presenter.
1 Activity/1Fragment -> 1 Controller-View.
Dispatches actions.
ControllerView
“Man in the middle” between UI and everything else.
Powered presenter.
1 Activity/1Fragment -> 1 Controller-View.
Dispatches actions.
Subscribes to state changes.
abstract class ControllerView(

protected val actionDispatcher: ActionDispatcher,

protected val stateDispatcher: StateDispatcher)

: Application.ActivityLifecycleCallbacks, Subscriber<State> {

protected fun <T : Action> dispatch(action: T) {

actionDispatcher.dispatch(action)

}



override fun onNext(data: State) {

if (isActivityRunning) handleState(state)

}



abstract fun handleState(state: State)

}
Action
Action
Are the intentions, what we want to do.
Action
Are the intentions, what we want to do.
They have metadata.
Action
Are the intentions, what we want to do.
They have metadata.
They will be reduced and/or applied.
sealed class Action

sealed class UpdateAction : Action() {

data class ReorderItemsAction(val items: List<Item>) : UpdateAction()



data class UpdateItemAction(val localId: String,

val text: String?,

val color: Color) : UpdateAction()



data class UpdateFavoriteAction(
val localId: String, val favorite: Boolean) : UpdateAction()



data class UpdateColorAction(
val localId: String, val color: Color) : UpdateAction()

}

Dispatcher
Dispatcher
Used to dispatch data. An action or a new state.
Dispatcher
Used to dispatch data. An action or a new state.
Subscribers subscribe to it, listening for a new action or state.
Dispatcher
Used to dispatch data. An action or a new state.
Subscribers subscribe to it, listening for a new action or state.
Subscribers can unsubscribe.
DISPATCHER
STATE DISPATCHER ACTION DISPATCHER
DISPATCHER
STATE DISPATCHER ACTION DISPATCHER
DISPATCHER
abstract class Dispatcher<T> {

private val subscriptions = ArrayList<Subscriber<T>>()



fun subscribe(subscriber: Subscriber<T>): Boolean =

subscriptions.add(subscriber)



fun unsubscribe(subscriber: Subscriber<T>): Boolean =

subscriptions.remove(subscriber)



fun isEmpty(): Boolean =

subscriptions.isEmpty()



open fun dispatch(data: T) =

subscriptions.forEach { it.onNext(data) }



fun isSubscribed(subscriber: Subscriber<T>): Boolean {

val isSubscribed = subscriptions.find { it == subscriber }

return isSubscribed != null

}

}
Store
Store
It has the State of the app.
Store
Is subscribed to handle Actions.
It has the State of the app.
Store
Is subscribed to handle Actions.
It will use its Reducers to reduce an Action and dispatch a new State.
It has the State of the app.
State
enum class Navigation {

ITEMS_LIST,

EDIT_ITEM

}

data class ItemsListScreen(

val items: List<Item> = emptyList())



data class EditItemScreen(val currentItem: Item = Item())



data class State(

val itemsListScreen: ItemsListScreen = ItemsListScreen(),

val editItemScreen: EditItemScreen = EditItemScreen(),

val navigation: Navigation = Navigation.ITEMS_LIST)
State is immutable.
Store Action Subscriber
open class StoreActionSubscriber(actionDispatcher: ActionDispatcher,

val stateDispatcher: StateDispatcher)

: ActionSubscriber(actionDispatcher) {


override fun reduce(action: Action) {

val currentState = stateDispatcher.state

val newState = when (action) {

is CreationAction -> CreationReducer.reduce(action, currentState)

is UpdateAction -> UpdateReducer.reduce(action, currentState)

is ReadAction -> ReadReducer.reduce(action, currentState)

is DeleteAction -> DeleteReducer.reduce(action, currentState)

is NavigationAction -> NavigationReducer.reduce(action, currentState)

}

stateDispatcher.dispatch(newState)

}

}
Reducer
Reducer
Reduces an action to create a new state.
Reducer
Reduces an action to create a new state.
Reduces every part of the state independently.
Reducer
Reduces an action to create a new state.
Reduces every part of the state independently.
The resulted new state will be dispatched.
REDUCER
STORE
ACTION
CURRENT STATE
REDUCER
STORE
ACTION
CURRENT STATE
REDUCER
STORE
ACTION NEW STATE
CURRENT STATE
abstract class Reducer<in T : Action> {



open fun reduce(action: T, currentState: State) =

with(currentState) {

currentState.copy(

itemsListScreen = reduceItemsListScreen(action, itemsListScreen),

editItemScreen = reduceEditItemScreen(action, editItemScreen),

navigation = reduceNavigation(action, navigation)

)

}
open fun reduceItemsListScreen(action: T, itemsListScreen: ItemsListScreen) =

itemsListScreen.copy(
items = reduceItemsCollection(action, itemsListScreen.items))



open fun reduceItemsCollection(action: T, currentItems: List<Item>) =

currentItems.findAndMap(

find = { shouldReduceItem(action, it) },

map = { changeItemFields(action, it) })
open fun reduceEditItemScreen(action: T, editItemScreen: EditItemScreen) =

editItemScreen.copy(

currentItem = reduceCurrentItem(
action, editItemScreen.currentItem))



open fun reduceCurrentItem(action: T, currentItem: Item) =

if (shouldReduceItem(action, currentItem))
changeItemFields(action, currentItem)

else currentItem

open fun shouldReduceItem(action: T, currentItem: Item) = false



open fun changeItemFields(action: T, currentItem: Item) = currentItem



open fun reduceNavigation(action: T, currentNavigation: Navigation) = currentNavigation


}
Models
STORE
STORE
MODELS
Models
STORE
STORE
MODELS
PRESENTATION
STORE MODELS +
EXTENSIONS
Models
STORE
STORE
MODELS
PRESENTATION
STORE MODELS +
EXTENSIONS
Models
PERSISTENCE
PERSISTENCE
MODELS
MAPPER
enum class Color {

RED, YELLOW, GREEN, BLUE, WHITE

}



data class Item(

val localId: String = generateLocalId(),

val text: String? = null,

val favorite: Boolean = false,

val color: Color = Color.WHITE,

val position: Long = object : PositionsFactory {}.newPosition())
Store Models
enum class Color {

RED, YELLOW, GREEN, BLUE, WHITE

}



@RealmClass

open class Item() : RealmModel {

constructor(localId: String, text: String?, favorite: Boolean = false,

colorEnum: Color = Color.WHITE, position: Long) : this() {

this.localId = localId

this.text = text

this.favorite = favorite

this.color = colorEnum.name

this.position = position

}
Persistence Models
@PrimaryKey open var localId: String = ""

open var text: String? = null

open var favorite: Boolean = false

@Ignore private var colorEnum: Color = Color.WHITE

open var color: String = colorEnum.name

open var position: Long = 0



fun getColorAsEnum(): Color = Color.valueOf(color)



fun setColorAsEnum(color: Color) {

this.color = color.name

}
//Equals and hashcode since Realm doesn’t allow data classes
}
Persistence Models (2)
import com.cesarvaliente.kunidirectional.persistence.Color as PersistenceColor

import com.cesarvaliente.kunidirectional.persistence.Item as PersistenceItem

import com.cesarvaliente.kunidirectional.store.Color as StoreColor

import com.cesarvaliente.kunidirectional.store.Item as StoreItem
Mapper
fun StoreItem.toPersistenceItem(): PersistenceItem =

with(this) {

PersistenceItem(localId, text, favorite, color.toPersistenceColor(), position)

}



fun StoreColor.toPersistenceColor(): PersistenceColor =

when (this) {

StoreColor.BLUE -> PersistenceColor.BLUE

StoreColor.GREEN -> PersistenceColor.GREEN

StoreColor.RED -> PersistenceColor.RED

StoreColor.WHITE -> PersistenceColor.WHITE

StoreColor.YELLOW -> PersistenceColor.YELLOW

}
Presentation models?
fun Color.toColorResource(): Int =

when (this) {

RED -> R.color.red

YELLOW -> R.color.yellow

GREEN -> R.color.green

BLUE -> R.color.blue

WHITE -> R.color.white

}



fun Item.getStableId(): Long = this.localId.hashCode().toLong()
Middleware
Middleware
We can have as much middleware as we want.
Middleware
We can have as much middleware as we want.
Middleware is subscribed listening for Actions.
Middleware
We can have as much middleware as we want.
Middleware is subscribed listening for Actions.
Middleware can return a new Action after “apply” the previous one.
ACTION DISPATCHER
ACTION DISPATCHER
MIDDLEWARE 1
ACTION DISPATCHER
MIDDLEWARE 1
ACTION DISPATCHER
MIDDLEWARE 1
ACTION DISPATCHER
MIDDLEWARE 1
MIDDLEWARE 2
ACTION DISPATCHER
MIDDLEWARE 1
MIDDLEWARE 2
ACTION DISPATCHER
MIDDLEWARE 1
MIDDLEWARE 2
ACTION DISPATCHER
MIDDLEWARE 1
MIDDLEWARE N
MIDDLEWARE 2
.
.
.
ACTION DISPATCHER
MIDDLEWARE 1
MIDDLEWARE N
MIDDLEWARE 2
.
.
.
ACTION DISPATCHER
MIDDLEWARE 1
MIDDLEWARE N
MIDDLEWARE 2
.
.
.
Threading?
VIEW STORE PERSISTENCE
MIDDLEWARE
Threading?
VIEW STORE PERSISTENCE
MIDDLEWARE
UI Thread
Threading?
VIEW STORE PERSISTENCE
MIDDLEWARE
UI Thread Store Thread
Threading?
VIEW STORE PERSISTENCE
MIDDLEWARE
UI Thread Store Thread
Persistence
Thread
(MiddlewareN Thread)
Testing?
Testing?
UI tests on Presentation layer (View).
Testing?
UI tests on Presentation layer (View).
Unit tests + Integration tests on our ControllerViews.
Testing?
UI tests on Presentation layer (View).
Unit tests + Integration tests on our ControllerViews.
Acceptance tests in our Persistence layer (middleware).
Testing?
UI tests on Presentation layer (View).
Unit tests + Integration tests on our ControllerViews.
Acceptance tests in our Persistence layer (middleware).
Unit tests in our Store layer.
Demo time(1)!
Demo time(1)!
FETCH LIST
ITEMS
LIST ITEMS
Action
State
Demo time(1)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH LIST
ITEMS
LIST ITEMS
Action
State
Demo time(1)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
EMPTY ITEM
FETCH LIST
ITEMS
LIST ITEMS
Action
State
Demo time(1)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
EMPTY ITEM
SAVE ITEM NEW ITEM
FETCH LIST
ITEMS
LIST ITEMS
Action
State
Demo time(1)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
EMPTY ITEM
SAVE ITEM NEW ITEM
TO NOTE LIST NAVIGATION UPDATED
FETCH LIST
ITEMS
LIST ITEMS
Action
State
Demo time(1)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
EMPTY ITEM
SAVE ITEM NEW ITEM
TO NOTE LIST NAVIGATION UPDATED
FETCH LIST
ITEMS
LIST ITEMS
FETCH LIST
ITEMS
LIST ITEMS
Action
State
Demo time(2)!
Demo time(2)!
OPEN EDIT ITEM NAVIGATION UPDATED
Action
State
Demo time(2)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
CURRENT ITEM
Action
State
Demo time(2)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
CURRENT ITEM
CHANGE COLOR CURRENT ITEM x 6
Action
State
Demo time(2)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
CURRENT ITEM
SAVE ITEM ITEM UPDATED
CHANGE COLOR CURRENT ITEM x 6
Action
State
Demo time(2)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
CURRENT ITEM
SAVE ITEM ITEM UPDATED
TO NOTE LIST NAVIGATION UPDATED
CHANGE COLOR CURRENT ITEM x 6
Action
State
Demo time(2)!
OPEN EDIT ITEM NAVIGATION UPDATED
FETCH ITEM
DETAILS
CURRENT ITEM
SAVE ITEM ITEM UPDATED
TO NOTE LIST NAVIGATION UPDATED
FETCH LIST
ITEMS
LIST ITEMS
CHANGE COLOR CURRENT ITEM x 6
Action
State
Demo time(3)!
Demo time(3)!
SET AS FAVORITE ITEM UPDATED x 3
Action
State
Demo time(4)!
Demo time(4)!
Demo time(4)!
REORDER ITEMS UPDATED x 2
Action
State
Demo time(5)!
Demo time(5)!
Action
State
DELETE ITEM ITEMS UPDATED x 2
Last demo!
The cool thing of middleware
in this architecture with
decoupled elements.
? @CesarValiente@CesarValiente
Thanks!
@CesarValiente@CesarValiente
Useful Links
KUnidirectional app: https://github.com/CesarValiente/KUnidirectional
KUnidirectional demo videos: https://goo.gl/PIfjr5
Flux: https://facebook.github.io/flux/
Redux: http://redux.js.org/
Flux on Android: https://github.com/lgvalle/android-flux-todo-app
License
(cc) 2017 César Valiente.
Some rights reserved.
This document is distributed under the Creative
Commons Attribution-ShareAlike 3.0 license, available in
http://creativecommons.org/licenses/by-sa/3.0/
Image licenses
Flux and Redux images are property of Facebook.
Emojis by Emoji One (CC-BY): http://emojione.com/

More Related Content

Similar to Cesar Valiente "Unidirectional architecture on Android with Kotlin"

ReSwift & Machine Learning
ReSwift & Machine LearningReSwift & Machine Learning
ReSwift & Machine LearningRodrigo Leite
 
Beginning Native Android Apps
Beginning Native Android AppsBeginning Native Android Apps
Beginning Native Android AppsGil Irizarry
 
HIT3328 - Chapter04 - Complex Interactions
HIT3328 - Chapter04 - Complex InteractionsHIT3328 - Chapter04 - Complex Interactions
HIT3328 - Chapter04 - Complex InteractionsYhal Htet Aung
 
Me and my importers
Me and my importersMe and my importers
Me and my importersDonny Wals
 
iOS Architectures
iOS ArchitecturesiOS Architectures
iOS ArchitecturesHung Hoang
 
Behavioral Design Patterns
Behavioral Design PatternsBehavioral Design Patterns
Behavioral Design PatternsLidan Hifi
 
MongoDB Mobile
MongoDB Mobile MongoDB Mobile
MongoDB Mobile MongoDB
 
What's New in Android
What's New in AndroidWhat's New in Android
What's New in AndroidRobert Cooper
 
Postgres Conf Keynote: What got you here WILL get you there
Postgres Conf Keynote: What got you here WILL get you therePostgres Conf Keynote: What got you here WILL get you there
Postgres Conf Keynote: What got you here WILL get you thereAnant Jhingran
 
Ray: A Cluster Computing Engine for Reinforcement Learning Applications with ...
Ray: A Cluster Computing Engine for Reinforcement Learning Applications with ...Ray: A Cluster Computing Engine for Reinforcement Learning Applications with ...
Ray: A Cluster Computing Engine for Reinforcement Learning Applications with ...Databricks
 
TKU行動APP開發管理實務 - ListView & Custom Adapter
TKU行動APP開發管理實務 - ListView & Custom AdapterTKU行動APP開發管理實務 - ListView & Custom Adapter
TKU行動APP開發管理實務 - ListView & Custom AdapterXavier Yin
 
Programming the Physical World with Device Shadows and Rules Engine
Programming the Physical World with Device Shadows and Rules EngineProgramming the Physical World with Device Shadows and Rules Engine
Programming the Physical World with Device Shadows and Rules EngineAmazon Web Services
 
Três conceitos que farão a diferença nos seus apps
Três conceitos que farão a diferença nos seus appsTrês conceitos que farão a diferença nos seus apps
Três conceitos que farão a diferença nos seus appsGuilherme Rambo
 
Hello, ReactorKit 
Hello, ReactorKit Hello, ReactorKit 
Hello, ReactorKit Suyeol Jeon
 
Cloud Roundtable | Amazon Web Services: Key = Iteration
Cloud Roundtable | Amazon Web Services: Key = IterationCloud Roundtable | Amazon Web Services: Key = Iteration
Cloud Roundtable | Amazon Web Services: Key = IterationCodemotion
 
Types Working for You, Not Against You
Types Working for You, Not Against YouTypes Working for You, Not Against You
Types Working for You, Not Against YouC4Media
 
HIT3328 - Chapter03 - Simple Interactive Apps
HIT3328 - Chapter03 - Simple Interactive AppsHIT3328 - Chapter03 - Simple Interactive Apps
HIT3328 - Chapter03 - Simple Interactive AppsYhal Htet Aung
 

Similar to Cesar Valiente "Unidirectional architecture on Android with Kotlin" (20)

ReSwift & Machine Learning
ReSwift & Machine LearningReSwift & Machine Learning
ReSwift & Machine Learning
 
Thinking in react
Thinking in reactThinking in react
Thinking in react
 
Beginning Native Android Apps
Beginning Native Android AppsBeginning Native Android Apps
Beginning Native Android Apps
 
Software-defined Networks as Databases
Software-defined Networks as DatabasesSoftware-defined Networks as Databases
Software-defined Networks as Databases
 
HIT3328 - Chapter04 - Complex Interactions
HIT3328 - Chapter04 - Complex InteractionsHIT3328 - Chapter04 - Complex Interactions
HIT3328 - Chapter04 - Complex Interactions
 
Me and my importers
Me and my importersMe and my importers
Me and my importers
 
Functional Reactive Programming
Functional Reactive ProgrammingFunctional Reactive Programming
Functional Reactive Programming
 
iOS Architectures
iOS ArchitecturesiOS Architectures
iOS Architectures
 
Behavioral Design Patterns
Behavioral Design PatternsBehavioral Design Patterns
Behavioral Design Patterns
 
MongoDB Mobile
MongoDB Mobile MongoDB Mobile
MongoDB Mobile
 
What's New in Android
What's New in AndroidWhat's New in Android
What's New in Android
 
Postgres Conf Keynote: What got you here WILL get you there
Postgres Conf Keynote: What got you here WILL get you therePostgres Conf Keynote: What got you here WILL get you there
Postgres Conf Keynote: What got you here WILL get you there
 
Ray: A Cluster Computing Engine for Reinforcement Learning Applications with ...
Ray: A Cluster Computing Engine for Reinforcement Learning Applications with ...Ray: A Cluster Computing Engine for Reinforcement Learning Applications with ...
Ray: A Cluster Computing Engine for Reinforcement Learning Applications with ...
 
TKU行動APP開發管理實務 - ListView & Custom Adapter
TKU行動APP開發管理實務 - ListView & Custom AdapterTKU行動APP開發管理實務 - ListView & Custom Adapter
TKU行動APP開發管理實務 - ListView & Custom Adapter
 
Programming the Physical World with Device Shadows and Rules Engine
Programming the Physical World with Device Shadows and Rules EngineProgramming the Physical World with Device Shadows and Rules Engine
Programming the Physical World with Device Shadows and Rules Engine
 
Três conceitos que farão a diferença nos seus apps
Três conceitos que farão a diferença nos seus appsTrês conceitos que farão a diferença nos seus apps
Três conceitos que farão a diferença nos seus apps
 
Hello, ReactorKit 
Hello, ReactorKit Hello, ReactorKit 
Hello, ReactorKit 
 
Cloud Roundtable | Amazon Web Services: Key = Iteration
Cloud Roundtable | Amazon Web Services: Key = IterationCloud Roundtable | Amazon Web Services: Key = Iteration
Cloud Roundtable | Amazon Web Services: Key = Iteration
 
Types Working for You, Not Against You
Types Working for You, Not Against YouTypes Working for You, Not Against You
Types Working for You, Not Against You
 
HIT3328 - Chapter03 - Simple Interactive Apps
HIT3328 - Chapter03 - Simple Interactive AppsHIT3328 - Chapter03 - Simple Interactive Apps
HIT3328 - Chapter03 - Simple Interactive Apps
 

More from IT Event

Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...IT Event
 
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"IT Event
 
Max Voloshin - "Organization of frontend development for products with micros...
Max Voloshin - "Organization of frontend development for products with micros...Max Voloshin - "Organization of frontend development for products with micros...
Max Voloshin - "Organization of frontend development for products with micros...IT Event
 
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "IT Event
 
Konstantin Krivlenia - "Continuous integration for frontend"
Konstantin Krivlenia - "Continuous integration for frontend"Konstantin Krivlenia - "Continuous integration for frontend"
Konstantin Krivlenia - "Continuous integration for frontend"IT Event
 
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"IT Event
 
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"IT Event
 
Vladimir Grinenko - "Dependencies in component web done right"
Vladimir Grinenko - "Dependencies in component web done right"Vladimir Grinenko - "Dependencies in component web done right"
Vladimir Grinenko - "Dependencies in component web done right"IT Event
 
Dmitry Bartalevich - "How to train your WebVR"
Dmitry Bartalevich - "How to train your WebVR"Dmitry Bartalevich - "How to train your WebVR"
Dmitry Bartalevich - "How to train your WebVR"IT Event
 
Aleksey Bogachuk - "Offline Second"
Aleksey Bogachuk - "Offline Second"Aleksey Bogachuk - "Offline Second"
Aleksey Bogachuk - "Offline Second"IT Event
 
James Allardice - "Building a better login with the credential management API"
James Allardice - "Building a better login with the credential management API"James Allardice - "Building a better login with the credential management API"
James Allardice - "Building a better login with the credential management API"IT Event
 
Fedor Skuratov "Dark Social: as messengers change the market of social media ...
Fedor Skuratov "Dark Social: as messengers change the market of social media ...Fedor Skuratov "Dark Social: as messengers change the market of social media ...
Fedor Skuratov "Dark Social: as messengers change the market of social media ...IT Event
 
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"IT Event
 
Алексей Рагозин "Java и linux борьба за микросекунды"
Алексей Рагозин "Java и linux борьба за микросекунды"Алексей Рагозин "Java и linux борьба за микросекунды"
Алексей Рагозин "Java и linux борьба за микросекунды"IT Event
 
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"IT Event
 
Наш ответ Uber’у
Наш ответ Uber’уНаш ответ Uber’у
Наш ответ Uber’уIT Event
 
Александр Крашенинников "Hadoop High Availability: опыт Badoo"
Александр Крашенинников "Hadoop High Availability: опыт Badoo"Александр Крашенинников "Hadoop High Availability: опыт Badoo"
Александр Крашенинников "Hadoop High Availability: опыт Badoo"IT Event
 
Leonid Vasilyev "Building, deploying and running production code at Dropbox"
Leonid Vasilyev  "Building, deploying and running production code at Dropbox"Leonid Vasilyev  "Building, deploying and running production code at Dropbox"
Leonid Vasilyev "Building, deploying and running production code at Dropbox"IT Event
 
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...IT Event
 
Mete Atamel "Resilient microservices with kubernetes"
Mete Atamel "Resilient microservices with kubernetes"Mete Atamel "Resilient microservices with kubernetes"
Mete Atamel "Resilient microservices with kubernetes"IT Event
 

More from IT Event (20)

Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
 
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
 
Max Voloshin - "Organization of frontend development for products with micros...
Max Voloshin - "Organization of frontend development for products with micros...Max Voloshin - "Organization of frontend development for products with micros...
Max Voloshin - "Organization of frontend development for products with micros...
 
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
 
Konstantin Krivlenia - "Continuous integration for frontend"
Konstantin Krivlenia - "Continuous integration for frontend"Konstantin Krivlenia - "Continuous integration for frontend"
Konstantin Krivlenia - "Continuous integration for frontend"
 
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
 
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
 
Vladimir Grinenko - "Dependencies in component web done right"
Vladimir Grinenko - "Dependencies in component web done right"Vladimir Grinenko - "Dependencies in component web done right"
Vladimir Grinenko - "Dependencies in component web done right"
 
Dmitry Bartalevich - "How to train your WebVR"
Dmitry Bartalevich - "How to train your WebVR"Dmitry Bartalevich - "How to train your WebVR"
Dmitry Bartalevich - "How to train your WebVR"
 
Aleksey Bogachuk - "Offline Second"
Aleksey Bogachuk - "Offline Second"Aleksey Bogachuk - "Offline Second"
Aleksey Bogachuk - "Offline Second"
 
James Allardice - "Building a better login with the credential management API"
James Allardice - "Building a better login with the credential management API"James Allardice - "Building a better login with the credential management API"
James Allardice - "Building a better login with the credential management API"
 
Fedor Skuratov "Dark Social: as messengers change the market of social media ...
Fedor Skuratov "Dark Social: as messengers change the market of social media ...Fedor Skuratov "Dark Social: as messengers change the market of social media ...
Fedor Skuratov "Dark Social: as messengers change the market of social media ...
 
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
 
Алексей Рагозин "Java и linux борьба за микросекунды"
Алексей Рагозин "Java и linux борьба за микросекунды"Алексей Рагозин "Java и linux борьба за микросекунды"
Алексей Рагозин "Java и linux борьба за микросекунды"
 
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
 
Наш ответ Uber’у
Наш ответ Uber’уНаш ответ Uber’у
Наш ответ Uber’у
 
Александр Крашенинников "Hadoop High Availability: опыт Badoo"
Александр Крашенинников "Hadoop High Availability: опыт Badoo"Александр Крашенинников "Hadoop High Availability: опыт Badoo"
Александр Крашенинников "Hadoop High Availability: опыт Badoo"
 
Leonid Vasilyev "Building, deploying and running production code at Dropbox"
Leonid Vasilyev  "Building, deploying and running production code at Dropbox"Leonid Vasilyev  "Building, deploying and running production code at Dropbox"
Leonid Vasilyev "Building, deploying and running production code at Dropbox"
 
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
 
Mete Atamel "Resilient microservices with kubernetes"
Mete Atamel "Resilient microservices with kubernetes"Mete Atamel "Resilient microservices with kubernetes"
Mete Atamel "Resilient microservices with kubernetes"
 

Recently uploaded

Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsRoshan Dwivedi
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 

Recently uploaded (20)

Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 

Cesar Valiente "Unidirectional architecture on Android with Kotlin"