diff --git a/base/src/main/java/com/nimroddayan/commitbrowser/common/ui/Redux.kt b/base/src/main/java/com/nimroddayan/commitbrowser/common/ui/Redux.kt
new file mode 100644
index 0000000..ebc550a
--- /dev/null
+++ b/base/src/main/java/com/nimroddayan/commitbrowser/common/ui/Redux.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020 Nimrod Dayan nimroddayan.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.nimroddayan.commitbrowser.common.ui
+
+import kotlinx.coroutines.channels.ConflatedBroadcastChannel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.asFlow
+
+interface Action
+
+typealias AsyncAction<State> = suspend (previousState: State, dispatch: (action: Action) -> Unit) -> Unit
+
+interface Reducer<State> {
+    fun reduce(previousState: State, action: Action): State
+}
+
+interface ReduxStore<State> {
+    val stateFlow: Flow<State>
+    fun getState(): State
+    fun dispatch(action: Action)
+    suspend fun dispatch(action: AsyncAction<State>)
+}
+
+class Store<State>(
+    initialState: State,
+    private val reducer: Reducer<State>
+) : ReduxStore<State> {
+    private var state: State = initialState
+    private val stateChannel = ConflatedBroadcastChannel<State>()
+
+    override val stateFlow = stateChannel.asFlow()
+
+    override fun getState(): State = state
+
+    override fun dispatch(action: Action) {
+        state = reducer.reduce(state, action)
+        stateChannel.offer(state)
+    }
+
+    override suspend fun dispatch(action: AsyncAction<State>) {
+        action(state, ::dispatch)
+    }
+}
diff --git a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListController.kt b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListController.kt
index 4ae68c9..d2ce551 100644
--- a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListController.kt
+++ b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListController.kt
@@ -20,16 +20,17 @@ import com.nimroddayan.commitbrowser.base.loading
 import com.nimroddayan.commitbrowser.commitlist.commitInfo
 import com.nimroddayan.commitbrowser.common.epoxy.ViewStateEpoxyController
 import com.nimroddayan.commitbrowser.common.ui.ViewState
-import javax.inject.Inject
 
-class CommitListController @Inject constructor() : ViewStateEpoxyController<CommitListViewState>() {
+class CommitListController(
+    private val commitListViewModel: CommitListViewModel
+) : ViewStateEpoxyController<CommitListViewState>() {
     override fun buildModels(state: ViewState<CommitListViewState>) {
         state.data.list.forEach {
             commitInfo {
                 id(it.sha)
                 commitInfo(it)
                 clickListener { model, _, _, _ ->
-                    state.data.onClick?.invoke(model.commitInfo())
+
                 }
             }
         }
diff --git a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListFragment.kt b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListFragment.kt
index 08b8c6b..1e31668 100644
--- a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListFragment.kt
+++ b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListFragment.kt
@@ -20,41 +20,49 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
-import androidx.lifecycle.Observer
 import androidx.lifecycle.lifecycleScope
 import androidx.recyclerview.widget.DividerItemDecoration
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.nimroddayan.commitbrowser.commitlist.R
 import com.nimroddayan.commitbrowser.commitlist.databinding.CommitListFragmentBinding
 import com.nimroddayan.commitbrowser.common.recyclerview.OnLoadMoreScrollListener
-import com.nimroddayan.commitbrowser.common.ui.BaseFragment
 import com.nimroddayan.commitbrowser.di.withFactory
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onEach
 import timber.log.Timber
 import javax.inject.Inject
 
 class CommitListFragment @Inject constructor(
     commitListViewModelFactory: CommitListViewModel.Factory,
-    commitListController: CommitListController,
     private val navigation: CommitListNavigation
-) : BaseFragment<CommitListViewState, CommitListViewModel, CommitListFragmentBinding>(
-    commitListController,
-    R.layout.commit_list_fragment
-) {
-    override val viewModel: CommitListViewModel by viewModels { withFactory(commitListViewModelFactory) }
+) : Fragment() {
+    private val viewModel: CommitListViewModel by viewModels { withFactory(commitListViewModelFactory) }
+    private lateinit var binding: CommitListFragmentBinding
+    private lateinit var controller: CommitListController
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        viewModel.commitItemClickedEvent.observe(this, Observer { commitInfo ->
-            lifecycleScope.launchWhenStarted {
-                Timber.d("Item with sha: %s was clicked", commitInfo)
-                navigation.navigateToCommitDetails(commitInfo)
-            }
-        })
+    init {
+        lifecycleScope.launchWhenStarted {
+            viewModel.stateFlow.onEach { commitListViewState ->
+                controller.setData(commitListViewState)
+            }.collect()
+        }
     }
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-        val view = super.onCreateView(inflater, container, savedInstanceState)
+//    override fun onCreate(savedInstanceState: Bundle?) {
+//        super.onCreate(savedInstanceState)
+//        viewModel.commitItemClickedEvent.observe(this, Observer { commitInfo ->
+//            lifecycleScope.launchWhenStarted {
+//                Timber.d("Item with sha: %s was clicked", commitInfo)
+//                navigation.navigateToCommitDetails(commitInfo)
+//            }
+//        })
+//    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        binding = CommitListFragmentBinding.inflate(inflater, container, false)
+        controller = CommitListController(viewModel)
 
         binding.recyclerview.apply {
             layoutManager = LinearLayoutManager(context)
@@ -66,9 +74,7 @@ class CommitListFragment @Inject constructor(
             )
             addOnScrollListener(object : OnLoadMoreScrollListener(resources.getInteger(R.integer.load_threshold)) {
                 override fun onLoadMore() {
-                    controller.currentData?.let {
-                        viewModel.loadData(it.data.page + 1)
-                    }
+                    viewModel.loadCommits(viewModel.getState().data.page + 1)
                 }
             })
             adapter = controller.adapter
@@ -82,7 +88,7 @@ class CommitListFragment @Inject constructor(
             }
         }
 
-        return view
+        return binding.root
     }
 
     override fun onSaveInstanceState(outState: Bundle) {
diff --git a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListModels.kt b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListModels.kt
index 02cdccf..771102d 100644
--- a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListModels.kt
+++ b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListModels.kt
@@ -17,9 +17,8 @@
 package com.nimroddayan.commitbrowser.home.commitlist
 
 data class CommitListViewState(
-    val page: Int,
-    val list: List<CommitInfo>,
-    val onClick: ((commitInfo: CommitInfo) -> Unit)? = null
+    val page: Int = 0,
+    val list: List<CommitInfo> = emptyList()
 )
 
 data class CommitInfo(
diff --git a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListModule.kt b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListModule.kt
index ff8cf89..3a1a644 100644
--- a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListModule.kt
+++ b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListModule.kt
@@ -16,9 +16,12 @@
 package com.nimroddayan.commitbrowser.home.commitlist
 
 import androidx.fragment.app.Fragment
+import com.nimroddayan.commitbrowser.common.ui.Store
+import com.nimroddayan.commitbrowser.common.ui.ViewState
 import com.nimroddayan.commitbrowser.di.FragmentKey
 import dagger.Binds
 import dagger.Module
+import dagger.Provides
 import dagger.multibindings.IntoMap
 
 @Module
@@ -27,4 +30,14 @@ abstract class CommitListModule {
     @IntoMap
     @FragmentKey(CommitListFragment::class)
     abstract fun bindCommitListFragment(fragment: CommitListFragment): Fragment
+
+    companion object {
+        @Provides
+        fun provideStore(commitListReducer: CommitListReducer): Store<ViewState<CommitListViewState>> {
+            return Store(
+                initialState = ViewState.Loading(CommitListViewState()),
+                reducer = commitListReducer
+            )
+        }
+    }
 }
diff --git a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListReducer.kt b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListReducer.kt
new file mode 100644
index 0000000..f0c996e
--- /dev/null
+++ b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListReducer.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Nimrod Dayan nimroddayan.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.nimroddayan.commitbrowser.home.commitlist
+
+import com.nimroddayan.commitbrowser.common.ui.Action
+import com.nimroddayan.commitbrowser.common.ui.Reducer
+import com.nimroddayan.commitbrowser.common.ui.ViewState
+import javax.inject.Inject
+
+class CommitListReducer @Inject constructor() : Reducer<ViewState<CommitListViewState>> {
+    override fun reduce(previousState: ViewState<CommitListViewState>, action: Action): ViewState<CommitListViewState> {
+        return when (action) {
+            is FetchCommitListAction -> when (action) {
+                is FetchCommitListAction.Loading -> ViewState.Loading(previousState.data.copy(page = action.page))
+                is FetchCommitListAction.Success -> ViewState.Loaded(previousState.data.copy(list = previousState.data.list + action.commits))
+                is FetchCommitListAction.Failure -> ViewState.Error(action.throwable, previousState.data)
+            }
+            else -> previousState
+        }
+    }
+}
diff --git a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListViewModel.kt b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListViewModel.kt
index b1b98f9..365f9f7 100644
--- a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListViewModel.kt
+++ b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/CommitListViewModel.kt
@@ -16,18 +16,16 @@
 
 package com.nimroddayan.commitbrowser.home.commitlist
 
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.viewModelScope
 import com.nimroddayan.commitbrowser.api.GithubApi
-import com.nimroddayan.commitbrowser.common.coroutines.withInternet
 import com.nimroddayan.commitbrowser.common.network.InternetConnection
 import com.nimroddayan.commitbrowser.common.ui.BaseViewModel
+import com.nimroddayan.commitbrowser.common.ui.ReduxStore
+import com.nimroddayan.commitbrowser.common.ui.Store
+import com.nimroddayan.commitbrowser.common.ui.ViewState
 import com.nimroddayan.commitbrowser.di.CoroutinesDispatcherProvider
-import com.nimroddayan.commitbrowser.model.CommitResponse
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -37,74 +35,30 @@ class CommitListViewModel(
     handle: SavedStateHandle,
     githubApi: GithubApi,
     dispatchers: CoroutinesDispatcherProvider,
-    internetConnection: InternetConnection
-) : BaseViewModel<CommitListViewState>(handle, githubApi, dispatchers, internetConnection) {
-    val commitItemClickedEvent: LiveData<CommitInfo>
-        get() = _commitItemClickedEvent
-    private val _commitItemClickedEvent = MutableLiveData<CommitInfo>()
-
-    private val commitList = mutableListOf<CommitInfo>()
+    internetConnection: InternetConnection,
+    private val store: Store<ViewState<CommitListViewState>>,
+    private val fetchCommitListActionCreator: FetchCommitListActionCreator
+) : BaseViewModel<CommitListViewState>(handle, githubApi, dispatchers, internetConnection),
+    ReduxStore<ViewState<CommitListViewState>> by store {
 
     init {
         Timber.d("Initializing")
         val page = handle[STATE_PAGE] ?: 0
-        loadData(page, page > 0)
+        loadCommits(page)
     }
 
-    fun loadData(page: Int, restoringState: Boolean = false) {
-        handle[STATE_PAGE] = page
-        notifyLoading(
-            CommitListViewState(
-                page = page,
-                list = commitList
-            )
-        )
+    fun loadCommits(page: Int) {
         viewModelScope.launch {
-            val pages = (if (restoringState) 0 else page)..page
-            val response = mutableListOf<CommitResponse>()
-            pages.forEach { currentPage ->
-                Timber.d("Fetching data for page: %d", currentPage)
-                response += withContext(io) {
-                    withInternet({ waitForInternetAndNotifyLoading(page) }, { reportErrorAndRetry(it, page) }) {
-                        githubApi.getCommits(currentPage)
-                    }
-                }
-            }
-            Timber.d("Data loaded for page %d", page)
-            notifyDataLoaded(
-                CommitListViewState(
-                    page = page,
-                    list = commitList.apply {
-                        addAll(response.map {
-                            CommitInfo(
-                                sha = it.sha,
-                                avatar = it.author?.avatarUrl ?: "",
-                                message = it.commit?.message ?: "",
-                                date = it.commit?.author?.date ?: "",
-                                author = it.author?.name ?: ""
-                            )
-                        })
-                    },
-                    onClick = _commitItemClickedEvent::setValue
-                )
-            )
+            dispatch(fetchCommitListActionCreator.create(page))
         }
     }
 
-    private suspend fun waitForInternetAndNotifyLoading(page: Int) {
-        waitForInternet()
-        notifyLoading(CommitListViewState(page, commitList))
-    }
-
-    private fun reportErrorAndRetry(throwable: Throwable, page: Int): Boolean {
-        notifyError(throwable, CommitListViewState(page = page, list = commitList))
-        return retryAfterError(throwable)
-    }
-
     class Factory @Inject constructor(
         githubApi: GithubApi,
         dispatchers: CoroutinesDispatcherProvider,
-        internetConnection: InternetConnection
+        internetConnection: InternetConnection,
+        private val store: Store<ViewState<CommitListViewState>>,
+        private val fetchCommitListActionCreator: FetchCommitListActionCreator
     ) : BaseViewModel.Factory<CommitListViewModel>(
         githubApi,
         dispatchers,
@@ -115,7 +69,9 @@ class CommitListViewModel(
                 handle,
                 githubApi,
                 dispatchers,
-                internetConnection
+                internetConnection,
+                store,
+                fetchCommitListActionCreator
             )
         }
     }
diff --git a/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/FetchCommitListAction.kt b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/FetchCommitListAction.kt
new file mode 100644
index 0000000..ede76e6
--- /dev/null
+++ b/commitlist/src/main/java/com/nimroddayan/commitbrowser/home/commitlist/FetchCommitListAction.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 Nimrod Dayan nimroddayan.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.nimroddayan.commitbrowser.home.commitlist
+
+import com.nimroddayan.commitbrowser.api.GithubApi
+import com.nimroddayan.commitbrowser.common.ui.Action
+import com.nimroddayan.commitbrowser.common.ui.AsyncAction
+import com.nimroddayan.commitbrowser.common.ui.ViewState
+import javax.inject.Inject
+
+sealed class FetchCommitListAction : Action {
+    class Loading(val page: Int) : FetchCommitListAction()
+    class Success(val commits: List<CommitInfo>) : FetchCommitListAction()
+    class Failure(val throwable: Throwable) : FetchCommitListAction()
+}
+
+class FetchCommitListActionCreator @Inject constructor(
+    private val githubApi: GithubApi
+) {
+    fun create(
+        page: Int
+    ): AsyncAction<ViewState<CommitListViewState>> {
+        return { previousState, dispatch ->
+            dispatch(FetchCommitListAction.Loading(page))
+            try {
+                val pages = (if (isRestoringSavedState(page, previousState)) 0 else page)..page
+                dispatch(
+                    FetchCommitListAction.Success(
+                        pages.map { currentPage ->
+                            githubApi.getCommits(currentPage).map {
+                                CommitInfo(
+                                    sha = it.sha,
+                                    avatar = it.author?.avatarUrl ?: "",
+                                    message = it.commit?.message ?: "",
+                                    date = it.commit?.author?.date ?: "",
+                                    author = it.author?.name ?: ""
+                                )
+                            }
+                        }.flatten()
+                    )
+                )
+            } catch (e: Exception) {
+                dispatch(FetchCommitListAction.Failure(e))
+            }
+        }
+    }
+
+
+    private fun isRestoringSavedState(
+        page: Int,
+        previousState: ViewState<CommitListViewState>
+    ) = page > 0 && previousState.data.list.isEmpty()
+}
