Compared to the TODO-MVP-RXJAVA, this project implements all business logic and presentation logic as pure functions, and utilizes Mobius to manage state and side-effects. Presenters are called ViewDataMappers and they are pure functions that accept
Model instances and return
ViewData objects that are then bound to the UI. Furthermore, views were extracted into separate classes used by fragments. Data Source implementations were reused.
TasksRepository class has been removed as its responsibility is achieved using a Mobius update function.
The project contains four features. Each feature's implementation is a little unique to show the different approaches to modeling in Mobius. Here are the highlights:
- Tasks List: This uses a Data class as the
Model. It also has a
ViewDataclass that encapsulates what the view should render. A pure function that maps
ViewDataones is applied for every
Modelupdate and the resulting
ViewDataobject is passed to the View for rendering.
- Add/Edit Task: Also uses a Data class as the
Modelbut binds that model directly to the View without the need for
- Task Details: Same as Tasks List
- Statistics: This uses a Sum Type as the
Modelto represent the different states.
Each feature is divided into the following package structure:
- featurename.domain: Contains all things describing the domain of the feature. The definition of the
Model, Logic (
Effects that describe the feature.
- featurename.effecthandlers: Contains the definition of all effect handlers that execute effects the logic functions dispatch.
- featurename.view: Contains all things that have to do with presentation logic (i.e. ViewDataMappers), the definition of ViewData types, and the views implementation.
- featurename: Contains the feature's Fragment/Activity that is responsible for creating a
MobiusLoop.Controller, connecting it to the View and managing it.
Features in this project treat
Fragments as builders that are only responsible for managing lifecycle and creating dependencies. Each
Fragment creates a
MobiusLoop.Controller and connects it to the view, which is now a separate
Views class, owned by the
Fragment. Once a
MobiusLoop.Controller is started, it'll start the
MobiusLoop which kicks things off by invoking the
Init function. The
Update functions define how state should evolve and what effects should happen. The UI is purely derived from the representation of state, i.e. the
Model. There are helper methods that turn
Bundles and vice versa. These are used for state restore.
Mobius uses a computation thread to process events, i.e. invoke
Update functions. This thread is synchronized and events are processed one at a time. It also utilizes an
ExecutorService to process effects returned by the logic functions.
MobiusLoop.Controller makes sure
Model updates are delivered on the main thread for rendering.
There's a helper builder accessible through
RxMobius.subtypeEffectHandler() that allows you to specify an effect handler per effect type. Here's the definition of the effect handlers for the AddEditTask feature
ObservableTransformer<AddEditTaskEffect, AddEditTaskEvent> effectHandler = RxMobius.<AddEditTaskEffect, AddEditTaskEvent>subtypeEffectHandler() .addAction(NotifyEmptyTaskNotAllowed.class, showEmptyTaskError, mainThread()) // where showEmptyTaskError is an Action implementation .addAction(Exit.class, showTasksList, mainThread()) // where showTasksList is an Action implementation .addFunction(CreateTask.class, createTaskHandler(remoteSource, localSource)) // where createTaskHandler returns an Function<CreateTask, AddEditTaskEvent> .addFunction(SaveTask.class, saveTaskHandler(remoteSource, localSource)) //where saveTaskHandler returns an Function<SaveTask, AddEditTaskEvent> .build();
For more information about Effect Handlers, please refer to the Mobius Wiki
- Mobius 1.2.0
- DataEnum 1.3.1
- AutoValue 1.6.0
- HamcrestPojo 1.1.1
- RxJava 2.x
- RxAndroid 2.x
- SqlBrite 2.x
Complexity - understandability
Use of architectural frameworks/libraries/tools:
Mobius simplifies building of business logic by turning it into a synchronous pure function.
A couple of new concepts that need to be learned such as Pure functions and Sum Types.
Very High. For the following reasons:
- Logic is written as pure functions which are the easiest form of functions to test. Consequently tests can be written in a BDD behavior specification style. Furthermore, TDD becomes a lot simpler since there's no need for mocks/fakes/stubs.
- Each effect handler has a single responsibility and the least amount of logic needed to perform the requested effect. This makes them a lot simpler and consequently easier to reason about and test.
UI testing can be split into two categories of tests:
- Presentation: Since presentation logic is also defined as a Pure function, testing it becomes very simple.
- View: Same as TODO-MVP-RXJAVA. This implementation does not change anything with regards to integration/end to end testing.
Ease of amending or adding a feature
- Low as Pure functions are easy to write/test
- Medium as RxJava is not trivial. There are, however, several utilities in the framework that help build RxJava chains for users. It's worth mentioning that Mobius as a framework can also be used in apps that do not utilize RxJava.
This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.