I've been doing something similar to this, except with go. In my case I have a flutter frontend and a go backend that's built using go mobile. Instead of trying to figure out how to make all of my go functions use data types that are supported by the various native frameworks, I've opted to use protobuf objects for every type that is shared between the frontend and backend. This way I can expose a single go function via the flutter FFI that takes in a binary array and then converts it to a protobuf object. This gives me a nice separation of concerns between my business logic and frontend while also providing easy to use objects for the front and backend.
Not sure that I'd recommend this approach to everyone. Protobuf code generation can be finicky to set up, but I'm doing it so that I can access go's rich array of libraries in my app.
Have you considered just using gRPC in this case? You gain 100% language separation (no FFI) and remote client/server at the cost of a little more call overhead.
Hah yea. I just did a deep dive into protobufs and RPC for an embedded application. Left learning a lot, and with a headache. Part of it was because this was using heapless, and I got errors until I configured the generator to use the right Vec sizes.
That's a perfectly fine approach, Protobuf strength is exactly these kind of use cases.
This is what I did for BeatScratch! https://beatscratch.io
My music model is all Protobuf messages, which go from Dart/Flutter land to Kotlin/C/Swift/JS audio backends on target platforms. I also use Protobuf for saving and sharing. It’s been incredibly resilient and performant.