# Cleaner Flutter Vol. 7: Where the data comes from

# Where we left

The previous volume began with our data layer, a layer where we implement all the actions described in the domain layer to be able to interact with an external service, or a local service (some database on the device or contact with the hardware where we run).

![Data Layer in CleanScope](https://cdn.hashnode.com/res/hashnode/image/upload/v1644423846763/IHlBFz7TS.png)

As the title says, let's talk about where the data comes from. We already have our models where we establish what data we are going to use, now we have to go and bring them.

We call classes `DataSource` and they bring us the "raw" data, convert it into the respective models and pass it to their repository. But there are also other nomenclatures, as in the Bloc library, they are called [`DataProvider`](https://bloclibrary.dev/#/architecture?id=data-provider).

You can call it whatever you want, it seems to me that the name `DataProvider` would be better if `Providers` of these classes were created.

Since we don't create `Providers` for `DataSources` in `riverpod`, the name is enough for me.

More of this is in the article on business logic.

# The source of the data

To define what a `DataSource` is, I'm going to borrow the concept of `DataProvider` from [bloclibrary.dev](https://bloclibrary.dev/)...

> They provide raw data, they should be generic and versatile. They will usually expose simple APIs to perform CRUD operations.

When I use `DataSources` I separate them into two folders: local and remote. This is because `DataSources` are a way to bring information, which as I mentioned before can be on the same device or to an external API where we have to use HTTP or some other internet protocol.

![DataSources folder structure](https://cdn.hashnode.com/res/hashnode/image/upload/v1644423848453/I5mx9X0lK.png)

A common question is why we don't use repositories to do everything the `DataSource` does. Mainly because many possible actions are carried out with the data that we want to bring.

A case that happens to me quite often is that I want to save some data in a local database --my preferred option is [`hive`](https://docs.hivedb.dev/#/)-- and I bring the data from a REST API. The save-to-device action and the API call action are two different functions. In addition to being two actions that are executed in two different data sources.

Given the above, my repository is in charge of establishing this flow with two different data sources and is in charge of handling the data returned by various data sources and subsequently communicating that result to the business logic.

# Implementing a DataSource

## LocalDataSource

```dart
import 'dart:convert';

import 'package:hive/hive.dart' show Box;

abstract class ILocalDataSource {
  SomeModel getSomeModel();
  Future<void> saveSomeModel(SomeModel model);
}

class LocalDataSource implements ILocalDataSource {
  LocalDataSource({required Box box}) : _box = box;

  final Box _box;

  @override
  SomeModel getSomeModel() {
    try {
      final encodedJson = _box.get('model_key');
      final model = SomeModel.fromJson(json.decode(encodedJson));
      return model;
    } catch (_) {
      throw CacheException();
    }
  }

  @override
  Future<void> saveSomeModel(SomeModel model) async {
    try {
      await _box.put('model_key', json.encode(model.toJson()));
    } catch (_) {
      throw CacheException();
    }
  }
}
```

This is what a `DataSource` looks like locally. I reuse this structure to store unique values in a `Box`, which is the equivalent of a table in SQL.

I always recommend that every class that you are going to test make an abstract class, so it is easier for them to make their mocks with packages like [`mocktail`](https://pub.dev/packages/mocktail).

In the same way, define the dependencies that you are going to use as properties of the class to only provide you with an object with which you can perform the actions you need. We will see this last point in more detail in the `RemoteDataSource`.

![https://cdn.hashnode.com/res/hashnode/image/upload/v1644423849930/rFyvqPMrx.gif](https://media.giphy.com/media/3orieNQgdcxedKjA3e/giphy.gif)

## RemoteDataSource

```dart
import 'package:dio/dio.dart';

abstract class IRemoteDataSource {
  Future<SomeModel> getModel(int modelId);
}

class RemoteDataSource implements IRemoteDataSource {
  RemoteDataSource({
    required Dio client
  }) : _client = client;

  final Dio _client;

  @override
  Future<SomeModel> getModel(int modelId) async {
    try {
      final response = await _client.get('/models/$modelId/');
      if (response.statusCode != 200) throw ServerException();
      return SomeModel.fromJson(response.data);
    } catch (e) {
      throw ServerException();
    }
  }  
}
```

Some packages make it much easier for us to structure our code and [`package:dio`](https://pub.dev/packages/dio) is one of them. If you want to learn more about `dio`, check [here](https://marcossevilla.dev/dio).

It is a very similar structure to the `LocalDataSource`. We have a dependency injection pattern where we still define dependencies as properties and pass an instance to our private property. All equal.

The difference lies in the implementation of how we get the data, one accesses the database with `hive` and the other goes to a remote API to take this data through `dio` (HTTP).

### Other implementations

For both types of `DataSources`, we can have different implementations so we make an interface that we can implement and define the body of the methods that we are going to use.

```dart
import 'package:http/http.dart' show Client;

class AnotherRemoteDataSource implements IRemoteDataSource {
  AnotherRemoteDataSource({
    required Uri url,
    required Client client,
  })   : _url = url,
        _client = client;

  final Uri _url;
  final Client _client;

  @override
  Future<SomeModel> getModel() async {
    try {
      final result = await _client.get(_url);
      if (response.statusCode != 200) throw ServerException();
      final decode = json.decode(result.body);
      return SomeModel.fromJson(decode);
    } catch (e) {
      throw ServerException();
    }
  }
}
```

This other implementation uses Dart's official `http` package and we can see that the result will be the same, only with a different way of doing it with another package.

There are 2 great advantages that [`package:dio`](https://pub.dev/packages/dio) offers us compared to HTTP. One is decoding our response from a pure JSON string to a Dart `Map <String, dynamic>`. We'll look at the other one in the next section.

# Reusing code

As you have seen from previous articles, CleanScope focuses on developing a project based on specific features. By abstracting our data logic into `DataSources` and `Repositories` we can arrive at a structure that, combined with `dio`, is very organized.

The following example is following how the creation of our data layer to be used as a dependency for our project would look like using [`flutter_bloc`](https://pub.dev/packages/flutter_bloc):

```dart
void main() {
  final localDataSource = LocalDataSource(box: Box());
  final remoteDataSource = RemoteDataSource(
    client: Dio(BaseOptions(AppConfig.apiUrl)),
  );

  final someRepository = SomeRepository(
    localDataSource: localDataSource,
    remoteDataSource: remoteDataSource,
  );

  runApp(App(someRepository: someRepository));
}

class App extends StatelessWidget {
  const App({required this.someRepository});

  final SomeRepository someRepository;

  @override
  Widget build(BuildContext context) {
    return RepositoryProvider.value(
      value: someRepository,
      child: const AppView(),
    );
  }
}
```

In the following article, we are going to go into detail about what the repository already implemented looks like, but for the moment we know that it receives its 2 `DataSources` as properties.

`dio` allows us to reuse instances of its clients with specific configurations so that we make direct calls to endpoints without specifying a base URL. This operation can be seen in the `GET` of the `RemoteDataSource` that I built previously with `dio`:

```dart
final response = await _client.get('/models/$modelId/');
```

That's one of the ways our `DataSources` are best used.

![https://cdn.hashnode.com/res/hashnode/image/upload/v1644423851664/uevnjlUhO.gif](https://media.giphy.com/media/QoesEe6tCbLyw/giphy.gif)
