Communication System 💬¶
Introduction¶
The communication system is intended to be used as a way of communication between the differents equipo's packages of the la aplicación project. The equipo de arquitectura defined 4 types of communication grouped in 3 categories:
- Router level: Used to delegate an entire flow to other team package, specifying the
re_directpath to continue ones the invoked flow is finished. - Presentation level: Used to a widget from the other equipo package with all it underlying business logic.
- Data level: Used to request data from the other equipo package, which is provided in two ways:
- Sync: The data is returned as a result of the function call.
- Async: The data is returned as a stream, it is based on the Pub/Sub pattern.
Communication Diagram¶
Communication Types¶
ReDirect query params (Router level)¶
The equipo who needs to implement this communication will have a route declaration that cheks for the re_direct query param:
GoRoute(
path: 'detailsQueryParam',
builder: (BuildContext context, GoRouterState state) {
final redirect = state.queryParams['re_direct']!;
return DetailScreen(
redirect: redirect,
);
},
),
In case that a response is needed return a value:
context.pop(true)
The equipo who needs to invoque the above flow will add where to return in the re_direct query param:
GoRouter.of(context).go('/detailsQueryParam?re_direct=/home');
or if a response is needed:
final response = await GoRouter.of(context).go('/detailsQueryParam?re_direct=/home');
Widget Broker (Presentation level)¶
Suppose you want to expose a Button that opens the camera to get a QR code which one is needed by many others teams.
-
Create your equipo broker module if you don't actually have one:
class QrPaymentsModule extends BrokerModule {} final qrPaymentsBroker = QrPaymentsModule(); -
Create the broker
GetQrBroker:class GetQrBroker extends BrokerInterface { @override String get route => "widget/qrCode"; @override List<String>? get requiredParameters => ["width", "height", "color"]; @override void receive( ICDictionary? parameters, ValueNotifier<ICDictionary>? observer, Function(ICResponse)? response, ) { final width = parameters!["width"]!; final height = parameters["height"]!; final color = parameters["color"]!; response(ICWidgetResponse( status: ICStatusResponses.ok, data: const QrCodeWidget( width: width, height: height, color: color, ), )); } } -
Add the broker to your module:
final getQrBroker = GetQrBroker(); qrPaymentsBroker.addAll({ getQrBroker.route: getQrBroker, });
In order to Consume the widget GetQrBroker:
Broker.shared.callWidget(
parameters: {
"width": "100",
"height": "100",
"color": "black",
},
route: const ICDestination(route: 'widget/qrCode'),
callback: (response) {
if (response.status == ICResponseStatus.ok) {
final qrCodeWidget = response.data as Widget;
// do something
} else {
// do something
}
},
);
As you may noted you have to ask to the owner team the route and the required parameters to consume the service.
Service Broker (Data level)¶
Suppose you want to expose a service that make transfers to any other team who needs to consume it.
-
Create your equipo broker module:
class MakeTransferModule extends BrokerModule {} final transfersBroker = MakeTransferModule(); -
Create the broker
MakeTransferBroker:class MakeTransferBroker extends BrokerInterface { MakeTransferBroker(this.context); MakeTransferUseCase makeTransferUseCase; @override String get route => "transfers/make"; @override List<String>? get requiredParameters => ["user", "amount"]; @override void receive( ICDictionary? parameters, ValueNotifier<ICDictionary>? observer, Function(ICResponse)? response, ) { final response = makeTransferUseCase( user: parameters!["user"]!, amount: parameters["amount"]! ); // ICServiceResponse.ok means that the broker was found independently of the response. response(ICServiceResponse.ok( data: { "status": response.status, }, )); } } -
If your're using Riverpod, register the broker when
MakeTransferUseCaseis created":final makeTransferUseCaseProvider = Provider<MakeTransferUseCase>( (ref) { final makeTransferUseCase = MakeTransferUseCase(); final makeTransferBroker = MakeTransferBroker(makeTransferUseCase); transfersBroker.addAll({ makeTransferBroker.route: makeTransferBroker, }); mockBroker.registerAll(); return makeTransferUseCase; } );
In order to Consume a service MakeTransferBroker:
Broker.shared.callService(
parameters: {
"user": "Alejo Ocampo",
"amount": "1000",
},
route: const ICDestination(route: 'transfers/make'),
callback: (response) {
if (response.status == ICResponseStatus.ok) {
final status = response.data["status"];
// do something
} else {
// do something
}
},
);
As you may noted you have to ask to the owner team the route and the required parameters to consume the service.
Broadcast (Data level)¶
The broadcast communication is coordinated by the equipo de arquitectura because the data transmmitted needs a model and a singleton channel.
If the model defined by the equipo de arquitectura looks like this:
class UserEvent extends EventBusSimpleEvent {
final String name;
final String lastName;
final int age;
UserEvent({
required this.name,
required this.lastName,
required this.age,
});
}
There will a channel named UserBroadcastChannel as a singleton on the itti_flutter_broadcast package that can be listened like this:
UserBroadcastChannel().eventStream.listen((UserEvent user) {
// do something with the event
});
and can add events like this:
UserBroadcastChannel().eventSink.add(
UserEvent(
name: 'Homero',
lastName: 'Simpson',
age: 40,
),
);