Small component that encapsulates an application's scenario logic.
[Changelog](./CHANGELOG.md) | [License](./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
xSimple and easy to use APIxFully tested (100% coverage)xFully documentedxsealed_resultcompatible (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;
}
}
}
Libraries
- generic_usecase
- Small component that encapsulates an application's scenario logic.