Data Fetching - Further Discussion

Why do we need to make data fetching such a complex concept? It is about differences between synchronous and asynchronous operations.

Point in Time

Recall the rectangle example:

class Rectangle {
    var String text;
    var Color color;

    Rectangle({required this.text, required this.color});
}
cat
dog

Consider Rectangle extends from Unit and has a Paper called UpdateColorAndText, upon which Rectangle will update its text and color.

class UpdateColorAndText extends Paper {
    final String text;
    final Color color;

    UpdateColorAndText({required this.text, required this.color});
}

With the above 2 states, we can picture a rectangle with some text and color and then sequentially:

  • 1: processes an UpdateColorAndText with text of "cat" and color of red.

  • 2: processes an UpdateColorAndText with text of "dog" and color of blue.

Papers represent not only requests that units need to process to update their state but also a reflection of attributes; and even the whole states at a point in time.

At one point, UpdateColorAndText with text of "cat" and color of red reflects that the rectangle has text of "cat" and color of red. At a later point, UpdateColorAndText with text of "dog" and color of blue reflects that the rectangle has text of "dog" and color of blue.

The main concern is the "duration" of the transition; or the processing operation to update the text from "cat" to "dog", and the color from red to blue.

onUpdateColorAndText(
    UpdateColorAndText p,
    RectangleState s,
    SourceVerifier ifFrom,
) {
    s.text = p.text;
    s.color = p.color;
}

With synchronous operations, there would be no problem for this concern because no other operations can interfere with this process, which means there would be no state changes happening during the operation.

But it is more complicated with asynchronous ones. If the state updating operations contain some asynchronous functions and need to await them to complete, then the papers will now represent not just a point in time anymore. Moreover, there will be a possibility of other state changes happening during the operation.

Attribute and State Updating

Let's say we don't make the NetworkDao extend from Unit. Instead, it is just a data service class and App will retrieve data in the conventional way.

class NetworkDao {
    Future<JsonCoinsList> fetchCoinsList(NetworkDaoParam param)
}

This way still works fine but it does not align with our idea of this pattern. How should fetchCoinsList be categorized?

  • Is it a state updating operation? It is not because it returns data; meanwhile a state updating operation should be a void method.

  • Is it an attribute? It can be but again it will fall within the problem mentioned in the Point in Time section.

Even not extending from Unit, we still recommend the concept of behavior as demonstrated in the previous section.

class NetworkDao {
    NetworkDao({this.onReturnCoinsList});

    final void Function(Result<JsonCoinsList, Exception> result)? onReturnCoinsList;

    Future<JsonCoinsList> fetchCoinsList(NetworkDaoParam param) {
        /// fetch data

        onReturnCoinsList(result);
    }
}

In this way, we will have more flexibility in handling state changes that happen during this process. For example, before the coins list is successfully fetched, App suddenly has to process another Paper, in which it needs to check if the coins list fetching is in progress or completed in order to handle something else, it will be more logical.

Suggestion

We understand that the above explanation probably sounds ambiguous and abstract. We will not make this approach a conventional rule. You can still use the simpler data service design if you are not comfortable and confident about our suggestion.

We believe that finding perfect solutions is impossible. Instead, be aware of what we will do and prepare for any problems that arise. Therefore, just be aware of this when working with this pattern and consider it when something similar to the above demonstration happens.