repo_case 0.1.2
repo_case: ^0.1.2 copied to clipboard

repo_case allows you to auto-generate use cases classes from your repository signature class

Repo Case #

GitHub: SandroMaglione Twitter: SandroMaglione

Automatically generate usecase classes from your repository class definition in Dart and Flutter.

Check out the official guide on repo_case and Clean Architecture #

Made by Sandro Maglione, check out his personal official website sandromaglione.com

Overview #

  1. Import the package (as well as the repo_case_generator and build_runner)
  2. Create a repository class
  3. Annotate the class with @repoCase
  4. Run the build
  5. Complete!

Quick tutorial #

Create your repository class definition in a new repository.dart file and annotate it with @repoCase:

@repoCase
abstract class UserRepository {
  String getString(int param);
}

Launch the build command to generate the usecase class:

flutter pub run build_runner build

The package will generate a usecase class for each method of the repository in a new repository.rc.dart file as follows:

class GetStringRepo {
  final UserRepository userRepository;

  const GetStringRepo({
    @required this.userRepository,
  });

  String call(GetStringRepoParams params) {
    return userRepository.getString(
      params.param,
    );
  }
}

class GetStringRepoParams {
  final int param;

  const GetStringRepoParams({
    @required this.param,
  });
}

You can now use the usecase class to access the repository:

/// Call repository with correct parameters
final String result = getStringRepo(
    GetStringRepoParams(
        param: 1,
    ),
);

Motivation #

This package is inspired by the by the Flutter TDD Clean Architecture Course from Reso Coder.

Specifically, the goal of the package is to autogenerate usecase classes. The template for the usecase class is presented in the 2nd video of the course.

The usecase class aims to abstract the domain layer from the presentation layer. For each method of the repository class we should write a new usecase class, which is then called from the presentation layer to access the repository.

Repository-Usecases-Presentation diagram

The template of each usecase class as presented in the course is basically always the same. The only differences are the name of the method, the parameters, and its return type. Perfect usecase for code generation!

Import the package #

Import the repo_case package as a normal dependencies and the repo_case_generator package as a dev_dependencies as well as the build_runner package (needed for code generation) in your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  repo_case: ^0.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter
    
  build_runner: any
  repo_case_generator: ^0.1.2

Clean Architecture #

Entity #

The entity class contains all the data exposed to the presentation layer, which comes from the data layer (database, local storage, etc.) and is accessed through the domain layer. The presentation layer will call the usecase class to access the entity to be displayed in the app.

We create a new domain folder and an entities folder inside it. We then create a entity class in a new entity.dart file inside the entities folder:

/// lib/domain/entities/entity.dart
class Entity {
  final String data;

  const Entity(this.data);
}

Model #

The models are used to fetch and convert external raw data (json, xml, etc.) to dart objects. These classes extends the entities in order to be passed from the data layer to the presentation layer.

We create a new data folder and a models folder inside it. We then create a model class in a new model.dart file inside the models folder:

/// lib/data/models/model.dart
class Model extends Entity {
  const Model(String data) : super(data);
}

Repository #

The repository contains the methods that will be called from the presentation layer to access the data from external sources. It defines all the usecases that can be accessed by the presentation layer. The repository will fetch the models from the data layer and return entities to the presentation layer.

We create a repository folder inside the domain folder. We then create a repository class in a new dart file. The file must contain an abstract class annotated with the @repoCase annotation:

/// lib/domain/repository/user_repository.dart
@repoCase
abstract class UserRepository {
  Entity getData(String param);
}

We can now run the build command to autogenerate the usecase classes:

flutter pub run build_runner build

This command will generate a new user_repository.rc.dart file in the same folder of the repository class (rc stays for 'repo_case'):

/// lib/domain/repository/user_repository.rc.dart
class GetDataRepo {
  final UserRepository userRepository;

  const GetDataRepo({
    @required this.userRepository,
  });

  Entity call(GetDataRepoParams params) {
    return userRepository.getData(
      params.param,
    );
  }
}

class GetDataRepoParams {
  final String param;

  const GetDataRepoParams({
    @required this.param,
  });
}

Repository concrete implementation #

We must also define the concrete implementation of the repository class that will fetch the model from the data layer and return the respective entity.

We create a new repository folder inside the data folder. We then create a new file containing the concrete implementation of our repository:

class UserRepositoryImpl implements UserRepository {
  @override
  Entity getData(String param) {
    /// Get data from data sources (database, local storage, etc.)
    return Model(param);
  }
}

Access the entity from the presentation layer #

We can now call the usecase class from the presentation layer to access the entities.

For example, we could have a bloc class that defines a method to fetch an entity to display in the app. Here below an example of how we would implement it:

class UserBloc {
  /// Usecase class generated by the repo_case package
  final GetDataRepo getDataRepo;

  const UserBloc(this.getDataRepo);

  Entity getDataFromRepository(String param) {
    return getDataRepo(
      /// Params class generated by the repo_case package
      GetDataRepoParams(
        param: param,
      ),
    );
  }
}

Injectable #

The package is designed to work with the injectable package.

All the usecase classes have the -Repo suffix. You can therefore add the following rule to your build.yaml file to automatically add the usecase classes to the list of classes generated by the injectable package. Check out the example for more details:

targets:
  $default:
    builders:
      injectable_generator:injectable_builder:
        options:
          auto_register: true
          class_name_pattern: "Repo$"

Roadmap #

I am always open for suggestions and ideas for possible improvements or fixes.

Feel free to open a Pull Request if you would like to contribute to the project.

If you would like to have a new feature implemented, just write a new issue.

Versioning #

  • v0.1.2 - 20 November 2020
  • v0.1.1 - 20 November 2020
  • v0.1.0 - 15 November 2020

License #

MIT License, see the LICENSE.md file for details.

4
likes
110
pub points
45%
popularity

Publisher

sandromaglione.com

repo_case allows you to auto-generate use cases classes from your repository signature class

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (LICENSE)

Dependencies

test

More

Packages that depend on repo_case