エンコーディングとスキーマ進化
🐱 この章の目次
エンコーディング形式の比較
データをネットワーク越しに送信したりディスクに保存したりするには、メモリ上のオブジェクトをバイト列に変換するエンコーディング(シリアライゼーション) が必要です。 『データ指向アプリケーションデザイン』第 4 章では、代表的な形式の特性を比較しています。
| 形式 | スキーマ | 人間可読 | サイズ | スキーマ進化 |
|---|---|---|---|---|
| JSON | 暗黙的(任意構造) | ○ | 大きい | フィールド追加は容易 |
| Protocol Buffers | 明示的(.proto) | × | 小さい | フィールド番号で互換性管理 |
| Avro | 明示的(.avsc) | × | 最小 | ライター/リーダースキーマの照合 |
JSON は手軽ですが、型情報が乏しく、数値の精度問題(整数が浮動小数点に丸められる)もあります。 バイナリ形式はサイズ効率と型安全性に優れ、大規模システムで好まれます。
スキーマ進化と互換性
システムが稼働し続ける限り、スキーマは変化します。 安全にスキーマを変更するためには、後方互換性(backward compatibility) と前方互換性(forward compatibility) の両方を意識する必要があります。
- 後方互換性: 新しいコードが古いデータを読める
- 前方互換性: 古いコードが新しいデータを読める
// Protocol Buffers でのスキーマ進化例
syntax = "proto3";
message UserEvent {
string user_id = 1;
string event_type = 2;
int64 timestamp_ms = 3;
// v2 で追加: 古いリーダーは未知フィールドとして無視する(前方互換性)
string source_ip = 4;
// v3 で追加: デフォルト値があるため古いデータでも読める(後方互換性)
int32 priority = 5;
}
フィールド番号を再利用しない、required フィールドを後から追加しないといったルールを守ることが互換性維持の鍵です。
API バージョニングとの関係
Part 2(API 設計)で学んだバージョニング戦略は、エンコーディングの互換性と密接に関係します。 REST API で JSON を使う場合、レスポンスに新しいフィールドを追加しても古いクライアントが壊れないようにする設計は、まさに前方互換性の実践です。 gRPC のように Protocol Buffers を使う場合は、.proto ファイルのフィールド番号規則がそのまま API の互換性ルールになります。
データフローのパターン
データが異なるプロセス間を流れる経路は、大きく 3 つに分類できます。
| パターン | 例 | 互換性の考慮点 |
|---|---|---|
| データベース経由 | 書き込みプロセスと読み取りプロセスが異なるバージョン | 古い行を新しいコードが読む(後方互換性) |
| サービス経由(REST / RPC) | マイクロサービス間の API 呼び出し | クライアントとサーバーが独立デプロイ |
| メッセージ経由 | Kafka、RabbitMQ などのメッセージブローカー | プロデューサーとコンシューマーが独立デプロイ |
いずれのパターンでも、送信側と受信側が異なるバージョンのスキーマで動く可能性を前提に設計します。 エンコーディング形式の選択は、このデータフロー全体の進化戦略を決定づける重要な設計判断です。