← Week 1: Design & Architecture

Day 3: API Design

Phase 7 · Sep 25, 2026

← Week 1: Design & Architecture

Agenda (2–3 hours)

  • Design (60 min): Define the protobuf service definition; apply gRPC best practices from Phase 3
  • Review (60 min): Evaluate the API against the access patterns; identify missing methods; add versioning strategy
  • Implement (60 min): Write the .proto file; run prost codegen; verify the generated types compile
← Week 1: Design & Architecture

Protobuf Definition

syntax = "proto3";
package taskqueue.v1;

service TaskQueue {
  rpc SubmitTask(SubmitTaskRequest) returns (SubmitTaskResponse);
  rpc GetTask(GetTaskRequest) returns (GetTaskResponse);
  rpc ListTasks(ListTasksRequest) returns (stream ListTasksResponse);
  rpc CancelTask(CancelTaskRequest) returns (CancelTaskResponse);
}

message SubmitTaskRequest {
  string idempotency_key = 1;  // client-generated UUID
  string task_type       = 2;  // e.g. "send_email", "generate_report"
  bytes  payload         = 3;  // opaque; task-type-specific
  int32  priority        = 4;  // 1 (low) to 5 (high)
}

message SubmitTaskResponse {
  string task_id    = 1;
  TaskStatus status = 2;
}
← Week 1: Design & Architecture

Task Status and Events

enum TaskStatus {
  TASK_STATUS_UNSPECIFIED = 0;
  PENDING                 = 1;  // submitted, not yet claimed
  PROCESSING              = 2;  // claimed by a worker
  COMPLETED               = 3;
  FAILED                  = 4;  // retryable failure
  DEAD                    = 5;  // exhausted retries → DLQ
  CANCELLED               = 6;
}

message TaskEvent {
  string    task_id    = 1;
  TaskStatus status    = 2;
  string    worker_id  = 3;  // set when status=PROCESSING
  string    error      = 4;  // set when status=FAILED
  int64     timestamp  = 5;  // Unix milliseconds
  int32     attempt    = 6;
}
← Week 1: Design & Architecture

Versioning Strategy

  • Package name includes version: taskqueue.v1
  • Breaking changes bump to taskqueue.v2; serve both simultaneously during migration
  • Non-breaking changes (new fields, new RPCs) are safe within v1
  • The API service routes based on the content-type: application/grpc+proto header version prefix
// Serve v1 and v2 from the same Tokio listener
let svc = tonic::transport::Server::builder()
    .add_service(TaskQueueV1Server::new(handler.clone()))
    .add_service(TaskQueueV2Server::new(handler));
← Week 1: Design & Architecture

Key Takeaways

  • Idempotency key in SubmitTask enables safe client retries — the API returns the same task_id for duplicate submissions
  • stream ListTasksResponse lets the API page results as a server-streaming RPC without buffering all tasks
  • Package versioning (v1, v2) is the safest gRPC breaking-change strategy
  • Opaque bytes payload decouples the API from task-type-specific schemas

Tomorrow: data model design — DynamoDB single-table schema for the task queue.