So far the Kladde talks to the world over HTTP and JSON: browsers, curl, the client from lesson 2.12.2. Between two services of your own there is a second way, one that is at home in .NET: gRPC. There the contract is not a description generated afterward but the starting point the code is generated from; the messages are binary and small; and every call carries a deadline. In this lesson you take the calculation of the free slots from lesson 2.5.6 and, as a trial, move it into a small service of its own, Kladde.Slots, which the web application asks over gRPC. It is an exercise: on its own the Kladde doesn't need a second service. In K3 you pick the service up again.

The contract in Protobuf

A gRPC service starts with a .proto file. It describes the service's methods and the messages they exchange, in the language Protocol Buffers (Protobuf). In the branch spike-grpc, create a new project and in it Protos/slots.proto:

PowerShell
dotnet new web -n Kladde.Slots -o src/Kladde.Slots -f net10.0
dotnet solution Kladde.slnx add src/Kladde.Slots/Kladde.Slots.csproj
dotnet add src/Kladde.Slots package Grpc.AspNetCore --version 2.85.0
protobuf
syntax = "proto3";

option csharp_namespace = "Kladde.Slots";

package kladde.slots.v1;

import "google/protobuf/timestamp.proto";

// The free slots of the office, computed from its opening hours and appointments.
service SlotService {
  // The free slots of one day.
  rpc GetFreeSlots (FreeSlotsRequest) returns (FreeSlotsReply);
  // The free slots of several days, one message per day as soon as it is computed.
  rpc StreamFreeDays (FreeDaysRequest) returns (stream FreeDay);
}

message FreeSlotsRequest {
  string day = 1;      // yyyy-MM-dd in the office's time zone
  int32 minutes = 2;   // length of one appointment
}

message FreeSlotsReply {
  repeated FreeSlot slots = 1;
}

message FreeDaysRequest {
  string first_day = 1;
  int32 days = 2;
  int32 minutes = 3;
}

message FreeDay {
  string day = 1;
  repeated FreeSlot slots = 2;
}

message FreeSlot {
  google.protobuf.Timestamp start = 1;
  int32 minutes = 2;
}
  • service names the methods. GetFreeSlots is unary: one request, one response. StreamFreeDays returns, with stream, a series of responses, one per day: server streaming.
  • message are the messages, with types like string, int32 and repeated for lists.
  • The number after each field is its field number. On the wire it isn't the name minutes that appears but the number 2. Everything further down about evolving the contract rests on that.
  • google.protobuf.Timestamp is a ready-made type for points in time. It knows no time zone, only an instant in UTC, like a DateTimeOffset without an offset. So you send the office's day as text, just as the API knows it in lesson 2.3.9 as a DateOnly.
  • package kladde.slots.v1 and csharp_namespace: the name of the service on the wire and the namespace in the C# code. The v1 in the package name is a convention for the day when an incompatible version is needed after all.