<<

Cross-Platform Data Models and API Using gRPC

Sebastian Hagedorn, Felix Lamouroux

SumUp. A better way to get paid Outline

1. Motivation & Goals 2. Choosing the Right Cross-Platform Technology 3. Introduction to and gRPC 4. Collaborative API Design 5. Here Be Dragons

SumUp. A better way to get paid Create a Playful & Performant App that Feels Native

SumUp. A better way to get paid Starting Point

● Green field: New project, clean slate, no legacy components, engineering-driven decisions ● Team of experienced developers per platform ● Preferred languages: iOS (Swift), Android (Kotlin), Web (Elm), Server-Backend (Go) ● No complex business logic on mobile/desktop clients

SumUp. A better way to get paid Goals

● Ensure consistent API usage ● Reduce boilerplate per platform ○ Parsing & ○ Transport layer ● No critical 3rd party dependencies above transport layer ● Integrate seamlessly with native languages & tools

SumUp. A better way to get paid Options Considered: JNI/Djinni

● Interfaces defined in platform-agnostic Interface Definition Language (IDL) ● Platform-specific interfaces (Java or C++/Objective-C) are generated ● Service/shared code must be implemented in C++ ● Decided against it: ○ Not usable for web client ○ Our team lacks C++ experience for large/critical code bases ○ Little client-side business logic in our UI-heavy project

https://github.com/dropbox/djinni

SumUp. A better way to get paid Options Considered: React Native

● Allows cross-platform code (way) beyond data model and networking ● Enables JavaScript developers to write native apps ● Decided against it: ○ Does not help to define a common model unless all components use React Native (backend must use node.js) ○ Our team consists of experienced platform developers (with little JS experience) ○ Does not align with our goal of playful & performant apps

SumUp. A better way to get paid Technology Stack: Protocol Buffers

● Binary serialization format developed by ● Model types are defined in simple Protobuf language ● Per-platform/per-language code is generated from Proto ○ Generates interface and implementation for (de)serialization ○ Official support for many languages: Java, Python, Objective-C, C++, Go, Ruby, and C# ○ More can be added via plugins, as Apple did for Swift: https://github.com/apple/swift-protobuf

SumUp. A better way to get paid Auto-generated types

Golang

Java Proto

Objective-C

Swift code generator available from Apple: https://github.com/apple/swift-protobuf

Swift SumUp. A better way to get paid Technology Stack: gRPC

● “A high performance, open-source universal RPC framework" (.io) ● API endpoints are defined in IDL (Protobuf by default) ● Server and client libraries are generated ○ Protobuf handles (de)serialization ○ gRPC handles networking ● Apps call generated endpoint APIs in native language ● gRPC-ProtoRPC generators & runtimes available in Swift and ObjC ○ We chose ObjC for better stability (Swift support is declared experimental) ○ Interdependency between Protobuf (Swift support implemented by Apple) and gRPC (Swift support implemented by official project maintainers): Cannot (necessarily) update components individually ○ Apple uses gRPC-ProtoRPC in private CloudKit server-to-server API

SumUp. A better way to get paid Code Speaks Louder than Words

SumUp. A better way to get paid Login Example: API Specs (Service)

▪ Support JSON fall-back via proxy ▪ Shared documentation also provided in auto-generated clients. ▪

SumUp. A better way to get paid Login Example: API Specs (Model)

SumUp. A better way to get paid Login Example: iOS Callsite (Swift)

SumUp. A better way to get paid Login Example: Backend Implementation (Go)

SumUp. A better way to get paid Collaborative API Design: Tooling & Setup

● Bridge gap between frontend & backend teams: Design API collaboratively ● Cross-platform API submodule ○ Central source of truth for model and API definitions (Proto files) ○ Tooling to generate code for all platforms (using Makefiles) ○ Integration of runtimes and generated files solved per platform repository ● All developers invited to contribute via Pull Requests to shared API specs ○ On-the-fly code generation allows local iterations ○ Review & verification on all platforms ● Shared vocabulary about types (user, account, profile, …)

SumUp. A better way to get paid Don't Let Clients Send With Ignored Fields

▪ Messages that are used in requests should only contain fields that are mutable by the client. ▪ Information about an entity that is only mutable by the server should only be available as part of responses. ▪ Combine messages to decorate them (composition over inheritance)

SumUp. A better way to get paid Avoid Duplicate Fields

Avoid repeating fields by wrapping decorating messages around existing ones.

SumUp. A better way to get paid Here Be Dragons

SumUp. A better way to get paid Beware: All Fields are Optional

Protocol buffers 3 no longer allows marking fields as required

▪ Makes backwards/forward-compatibility easier: ▪ required fields invalidate the entire message regardless of context ▪ parts of an application could pass around partially-complete messages internally ▪ whole applications might simply be interested in passing message through (see routers) ▪ Ensure that each callsite using a message performs sensible validation (no blanket rejections)

▪ Provide invalid default values to fields if you need to ensure that clients set them explicitly (see enums) ▪ Only validate messages when you care about the content

See also: https://capnproto.org/faq.html#how-do-i-make-a-field-required-like-in-protocol-buffers SumUp. A better way to get paid Optionality & Versioning

“We’re mitigating [backward and forward compatibility issues] by using Protocol Buffers and controlling most new features with flags, which remain turned off during a partition upgrade, and are enabled only after all hosts are updated.”

From Apple’s CloudKit paper (http://www.vldb.org/pvldb/vol11/p540-shraer.pdf)

SumUp. A better way to get paid Check Your Defaults

▪ Explicit UNINITIALIZED_* values in enums ensure that unset values are caught

▪ Check if your business logic makes sense for other type defaults, such as empty string or zero integers

SumUp. A better way to get paid Enforce Client-Side Non-Optional Values

▪ Linter enforces usage of “convenience” initializers ▪ Unit tests make sure that model changes do not go unnoticed

SumUp. A better way to get paid Tooling Quirks

● Custom Makefile to process Proto files ○ Not trivial to write ○ Integration is different per platform/IDE ● Make modifies output file timestamps regardless of content diff ○ Swift compiler re-builds the entire module ○ Solution: Extra script using rsync that only makes actual changes available to Xcode ● Integration of gRPC runtime is not trivial ○ …except when using CocoaPods ● Protobuf optionality is not reflected by ObjC header annotations ○ All properties are force-unwrapped

SumUp. A better way to get paid We Are Hiring

We are building a product team here in Cologne.

▪ Looking for iOS, Android, Web, Backend, UI/UX design…. ▪ Strong mobile focus with many senior developers.

http://sumup.com/careers/

SumUp. A better way to get paid Sebastian Hagedorn Felix Lamouroux iOS Lead Developer Project Lead - New Ventures (and iOS dev)

▪ @hagidd ▪ @felixlamouroux ▪ [email protected][email protected]

SumUp. A better way to get paid