Because Protbuf is not your typesystem
In Protobuff 3 all fields are optional, for example a proto file similar to:
messageA {
X x,
Y y,
Z y
}using a tool as ScalaPb to generate idiomatic Scala classes would generate something similar to this:
caseclassA(
x: Option[X],
y: Option[Y],
z: Option[Z]
)Leaking such class into the application would be very annoying, as it required developers to handle Options even when value is always going to be there (at least in this specific version of the protocol).
At Cakesolutions we are fans of Refined types, we use it in our model classes. While you can instruct ScalaPb to generate custom types (making use of Refined types?), this pollutes the proto file with Scala snippets, which defies the purpose of using proto files if you use multiple languages.
Refined allow you to own you model classes, which can use "Stricter" types, then get's automatically to converted from/to Proto buff (generated via ScalaPb)
Example: Given a protofile similar to
syntax="proto3";
packagecom.example.protocol;
import"google/protobuf/wrappers.proto";
messageEmployeeData {
// logically required (beware, ScalaPb sets default value to Zero)int32id=1;
// logically required, non-empty// beware: ScalaPb sets default value to empty string// However, our Refined types in the crossponding model should catch this.stringname=2;
// logically optional, Postive (using Google wrapper to allow optional)google.protobuf.Int32Valueage=20;
}and Scala class similar to
caseclassEmployeeData(
id: Int,
name: StringRefinedNonEmpty,
age: Option[IntRefinedPositive]
)
objectEmployeeData {
implicitvalprotocolBinding=Binding.instance[EmployeeData, com.example.protocol.employee.EmployeeData]
}Strcitify would be able to automatically Serialize and deserialize your class from/to crossponding Protobuff message.
importnet.cakesolutions.strictify.scalapb.ops.SerializationOps._valobj1=EmployeeData(1, "name", Some(99))
valbytes1= toByteArray(obj1)
valparsedObj1= parseFrom[EmployeeData](bytes1)
println(parsedObj1)