The purpose of this package, alongside its companion proto_annotations package, is to significantly reduce the workload of exposing Dart business model classes as protocol buffer messages to be used with gRPC.

Workflow

Let's assume we want to represent the concept of a recipe.

Without this package

One way of going about it is by creating a .proto file and include a message to represent the concept, and then run the protoc compiler to generate a dart file that represents the concept and serializes and deserializes itself to protobuffers to be used with gRPC.

There are limitations to this approach. One, for instance, is that .proto files do not support inheritance. We might also want to add business logic and validations to our business model, which will not be trivial, if at all possible, using the classes generated by protoc. Finally, there is no easy way to extend the compiled classes to add properties we actually do not want to serialize.

Another way of using protocol buffers is to go over the tedious work of typing our business model classes, adding whichever features we want to, typing .proto files containing message structures, generating dart classes that represent the .proto files and manually create functions that map from the business dart classes to the .proto classes.

With proto_annotations

With this package and proto_annotations, we get the flexibility of typing our dart business model, but without the tedious work of typing the matching .proto files and corresponding mapping code, eliminating a lot of busy work.

We begin by typing our business model. The next step is to use the proto_generator package to generate the corresponding .proto files, instead manually typing them. The proto_generator package also generates functions that map instances of the business model to instances of the corresponding classes that are generated by the protoc compiler. The final step is run the protoc compiler to generate the dart classes that represent the messages present in the generated .proto files.

Polymorphism

This package supports polymorphism. Here's an example:


@proto
@mapProto
class Car extends Vehicle {
  Car({
    required this.numberOfDoors,
    required int weight,
  }) : super(weight: weight);

  final int numberOfDoors;
}

@proto
@mapProto
class Airplane extends Vehicle {
  Airplane({
    required int weight,
    required this.wingspan,
  }) : super(weight: weight);

  final int wingspan;
}

// note that we are identifying all the
// known subclasses
const knownSubClasses = [
  Airplane,
  Car,
];

@Proto(knownSubClasses: knownSubClasses)
@MapProto(knownSubClasses: knownSubClasses)
abstract class Vehicle { // can be non-abstract as well
  final int weight;
  Vehicle({
    required this.weight,
  });
}

void main() {
  final car = Car(numberOfDoors: 4, weight: 1500);
  final Vehicle airplane = Airplane(wingspan: 13, weight: 1500);

  final vehicles = <Vehicle>[car, airplane];
  // from here we will get a list of maps containing
  // a serialization of each Vehicle
  final protos = vehicles.map((v) => v.toProto());

  // not that we call .toVehicle because we know 
  // they are all vehicles
  final deserVehicles = protos.map((m) => m.toVehicle()).toList();

  final deserCar = deserVehicles[0] as Car; // it is really a car
  final deserAirplane = deserVehicles[1] as Airplane; // and it is really an airplane

  print(deserCar.numberOfDoors); // should print 4
  print(deserAirplane.wingspan); // should print 13

}

Getting started

In order to get started, look at the example project at https://gitlab.com/squarealfa/dart_framework/-/tree/main/proto_mapper/example.

Context

This package is part of a set of losely integrated packages that constitute the SquareAlfa Dart Framework.

Libraries

proto_generator
Support for doing something awesome.