Behaviours are classes of which the instances are used as functions.
When writing code it is best to keep concerns separated and code testable. A behaviour helps with this. It is comparable with a global function which must be instantiated before it can be used. Because it is like a global function it only has one concern: executing one piece of logic, one behaviour. Because it still is a class, it can be injected where needed and/or mocked in testing.
A behaviour itself will never throw an exception. All exceptions/errors are
caught and returned in a FutureOr<ExceptionOr<TOut>> format, where the
ExceptionOr is either a Failed or a Success. The Failed holds the
exception in its reason property and the Success holds the return value, if
any, in its value property.
Behaviour<TIn, TOut>
The standard behaviour receives an input parameter of type TIn and returns a
TOut. If no output parameter is required it can be made void. A behaviour
returns when called a FutureOr<ExceptionOr<TOut>> value. For more details
about that look at the Return value.
class CreateCustomer extends Behaviour<CreateCustomerParams, void> {
@override
Future<void> action(CreateCustomerParams input, BehaviourTrack? track) {
// TODO logic to create a customer
return Future.value();
}
}
class CreateCustomerParams {
const CreateCustomerParams({
required this.name,
required this.address,
required this.phoneNumber,
});
final String name;
final String address;
final String phoneNumber;
}BehaviourWithoutInput<TOut>
This behaviour does not receive an input parameter and returns a TOut. If no
output parameter is required it can be made void. A behaviour returns when
called a FutureOr<ExceptionOr<TOut>> value. For more details about that look
at the Return value.
class GetProfileData extends BehaviourWithoutInput<ProfileData> {
@override
Future<ProfileData> action(BehaviourTrack? track) {
return Future.value(ProfileData(
name: 'Wim',
birthday: DateTime(1993, 10, 07),
));
}
}
class ProfileData {
const ProfileData({
required this.name,
required this.birthday,
});
final String name;
final DateTime birthday;
}The return value of a behaviour is always a FutureOr<ExceptionOr<TOut>> value.
ExceptionOr is a sealed class with two implementers: Failed<TSuccess> and
Success<TSuccess> with each a reason and value property respectively. To
determine what the received value is, the when method is provided (and
thenWhen for asynchronous results).
Future<void> main() async {
final getProfileData = GetProfileData();
await getProfileData().thenWhen(
(exception) => log('Exception: $exception'),
(value) => log('value: $value'),
);
}To monitor what the behaviour is doing a BehaviourMonitor exits. This is a
factory class which creates a BehaviourTrack. During the call of a behaviour,
the different methods of this track are called.
startwhen the action is executedendwhen the action has successfully been completed.stopWithExceptionwhen an exception happens.stopWithErrorwhen anything else is caught.
This monitor can be used to do logging/analytics/...