Design patterns using Rx in mobile solutions

 Design patterns using Rx in mobile solutions

In mobile applications, the design of functionality that involves the execution of multiple tasks could generate the following questions:

  • Will the execution of the task take a considerable time, 30 seconds, or more?
  • Is it required to notify the UI of the result of the task?
  • Are there dependencies with other tasks?
  • Are the results of the tasks an input for other tasks?
  • Should the task be executed with the application in the foreground, background, or both?
  • What happens to the execution of the task if the application is closed?
  • What should happen to the task execution if the device orientation changes?

These concerns should certainly be addressed in a design before starting implementation.

So, where to start?

The first thing to do is decide which actor should be delegated to the execution of the task. Should it be done on the frontend, on the device, or should it be done on the backend?

If the task involves considerable execution time, that is, processing times greater than 1 minute or even 30 seconds; it may be advisable to delegate the execution of the task to a backend component. This component could well be a serverless function (serverless function) through Lambdas or Cloud Functions, responsible for carrying out the transformation, validation, or respective lengthy task so that it subsequently notifies the frontend of the result.

It could also be if the tasks are scheduled and require long periods for their recurring execution.

On the other hand, there may be tasks requiring their execution on the device side either due to dependence on a peripheral or due to the device's capacity. For example:

  • Receive location updates via GPS.
  • Play audio, capture sounds or capture video for some time.
  • Query or debug a local persistence mechanism.
  • Internal timers for session control.

For these cases, in whose orchestration and choreography of the tasks are delegated to the frontend, I could recommend the following patterns.

Fan-out Pattern

This pattern is widely used in solutions that apply the Serverless Architecture style. Your goal is to amplify an event into multiple events to obtain the desired result.

The following figure shows an example diagram of such a pattern:

Analyzing that pattern's purpose, one could find similarities with how tasks are usually coordinated through Rx extensions applying reactive and functional programming.

Applying multicasting through Rx, the interactions between tasks are obtained, as shown in the figure:

The Rx Chain component receives an event, and from this event, the execution of multiple independent tasks is triggered. Such tasks could deliver results to the UI or apply some transformation or application logic without communicating with the UI.

The Rx Chain component is nothing more than the definition of an Observable with its respective chain of operators that could apply multiple filters and transformations.

And what is the relationship with the fan-out pattern?

Well, with a design variant to introduce an Rx Chain made up of a Subject instead of an Observable, it could be possible to amplify the received event and propagate it in multiple tasks as shown in the following diagram:

It is worth noting that a Subject component should only be used when necessary. It is advisable always to have the use of an Observable as the first option. Only in cases where a component is required, that fulfills a proxy's function capable of receiving and propagating events is valid. A Subject is the mutable version of an Observable; therefore, its use must be strictly necessary to not fall into the application of an anti-pattern that could degrade the design.

Additionally, for the application of the pattern to be consistent, it is recommended that the tasks meet the following conditions:

  • The tasks should not nest Rx code, this to avoid infinite cycle scenarios.
  • The defined task is independent; it does not depend on other tasks.
  • The task only supports execution in the foreground; that is, if the application is closed or the thread on which the task is executed is terminated, the operation's state is not maintained.

This design pattern is appropriate to apply when the related tasks do not need to be executed in the background that the operation's execution persists. If the application closes or goes to the background, it does not need to keep executing them.

What to do if you need to run background tasks and maintain their operation even when the application is closed?

The following design pattern is suitable for this type of requirement.

Task Emitter Pattern

This design pattern can be applied to orchestrate and decouple the UI from operations that need to be executed for an extended time on the frontend. Regardless of whether the application is in the foreground or background, its execution maintains its execution. At the end of the operation, a notification is sent to the UI for its respective management.

Rx Chain component allows you to orchestrate and applies transformations before launching long tasks.

The operations carried out by Rx Chain are designed to be executed in the foreground; this is important to bear in mind. In comparison, long tasks could be designed to run in both the foreground and background.

It is essential to keep in mind that Rx Chain does not depend on or wait for background tasks. At this point, decoupling must be maintained.

Task Executor is the component responsible for executing the long task, which may be executed in the background or foreground. Android and iOS have their respective APIs available to implement the design. Android can use, WorkManager, Foreground Service, AlertManager. In the case of iOS, BackgroundTasks Framework can be used.

On the other hand, there is the Notification Center, a component in charge of receiving the results of background operations and notifying the result's view component. Notification Center could correspond to each API's notification system on Android and iOS, or it could be a cloud service such as FCM or SNS.

A more elaborate design could include a component responsible for managing communication between executors of background tasks. It could even be a generic Task Executer dedicated solely to working and implemented with the technologies mentioned above on both the Android side and the iOS side (WorkManager, BackgroundTasks).

It is recommended that the tasks meet the following conditions to maintain design consistency:

  • The task can delegate to the Rx Chain the application of any business rule before its execution.
  • Long tasks are subject to the restrictions imposed by each of the APIs. For example, the device is in charging mode, an internet connection, that the device is active without blocking, and conditions as specific as, for instance, that the time limit for executing a task should not exceed 30 seconds others.

To conclude, this design pattern is recommended to decouple the View components from those long-running tasks.

References and links of interest

Next Post »
Thanks for your comment