generic_usecase 1.3.0 copy "generic_usecase: ^1.3.0" to clipboard
generic_usecase: ^1.3.0 copied to clipboard

Small component that encapsulates an application's scenario logic.

Small component that encapsulates an application's scenario logic.

SDK: Dart & Flutter Maintained with Melos Pub.dev


[Changelog] | [License]


Introduction #

Playing around with the clean architecture, I often found myself rewriting the generic code of my usecases.

These class enable you to encapsulate your logic in an atomic elements that you can then inject and use throughout your application.

Features #

  • ✅ Simple and easy to use API
  • ✅ Fully tested (100% coverage)
  • ✅ Fully documented
  • sealed_result compatible (see sealed_result)

Available usecase types:

  • Usecase<Input, Output>
  • NoParamsUsecase<Output>
  • ResultUsecase<Input, Output, Failure>
  • NoParamsResultUsecase<Output, Failure>
  • StreamUsecase<Input, Output>
  • NoParamsStreamUsecase<Output>
  • StreamResultUsecase<Input, Output, Failure>
  • NoParamsStreamResultUsecase<Output, Failure>

Usage #

Simple usecase #

Let's say you want to add two numbers together, you can create a usecase like this:

class AdditionUsecase extends Usecase<int, int> {
  const AdditionUsecase();

  @override
  Future<int> execute(int params) async {
    return params + params;
  }
}

The execute method is the one that will be called when you call the call method on your usecase.

final usecase = AdditionUsecase();
final result = await usecase(2);
print(result); // 4

Checking preconditions #

You can add a precondition check to your usecase, which will be executed before the execute method:

class DivisionUsecase extends Usecase<(int, int), double> {
  const DivisionUsecase();

  @override
  FutureOr<PreconditionsResult> checkPrecondition((int, int)? params) {
    if (params == null) {
      return PreconditionsResult(isValid: false, message: 'Params is null');
    }

    if (params.$2 == 0) {
      return PreconditionsResult(isValid: false, message: 'Cannot divide by 0');
    }

    return PreconditionsResult(isValid: true);
  }

  @override
  Future<double> execute((int, int) params) async {
    return params.$1 / params.$2;
  }
}

Using a result #

You can use a result (see sealed_result) usecase to return a Result object instead of a raw value:

class DivisionResultUsecase extends ResultUsecase<(int, int), double, Failure> {
  const DivisionResultUsecase();

  @override
  FutureOr<PreconditionsResult> checkPrecondition((int, int)? params) {
    if (params == null) {
      return PreconditionsResult(isValid: false, message: 'Params is null');
    }

    if (params.$2 == 0) {
      return PreconditionsResult(isValid: false, message: 'Cannot divide by 0');
    }

    return PreconditionsResult(isValid: true);
  }

  @override
  Future<Result<double, Failure>> execute((int, int) params) async {
    return Result.success(params.$1 / params.$2);
  }

  @override
  Result<double, Failure> onException(UsecaseException e) =>
      Result.failure(Failure(e.message ?? ''));
}

You need to override the onException method to build the Failure object from the UsecaseException .

Using a stream #

You can use a stream usecase to return a Stream instead of a raw value:

class GeneratorUsecase extends NoParamsStreamUsecase<int> {
  const GeneratorUsecase();

  @override
  Stream<int> execute() async* {
    for (int i = 0; i < 10; i++) {
      yield i;
    }
  }
}
3
likes
160
pub points
55%
popularity

Publisher

verified publisherhugop.cl

Small component that encapsulates an application's scenario logic.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

meta, sealed_result

More

Packages that depend on generic_usecase