はじめに
「イベントのペイロード構造を変えたら下流のサービスが壊れた」——スキーマレジストリでイベントのスキーマをバージョン管理し、後方互換性を保ちながら進化させる設計をClaude Codeに生成させる。
CLAUDE.mdに設計ルールを書く
## イベントスキーマレジストリ設計ルール
- MINOR(1.x.0): 後方互換の追加(optional化、フィールド追加)
- MAJOR(2.0.0): 破壊的変更(フィールド削除、型変更)
- required追加はMINORでは禁止
- 送信前にスキーマ検証(無効なイベントを発行しない)
- 検証エラーはDead Letter Queueへ
生成される実装(抜粋)
export class SchemaRegistry {
register(eventType, version, schema) {
// 後方互換チェック(同一Majorバージョン内)
if (versions.length > 0 && sameMajor) {
// required追加は禁止
for (const field of newRequired) {
if (!oldRequired.has(field)) throw new SchemaCompatibilityError(
`Cannot add required field '${field}' in ${eventType}@${version}`
);
}
// フィールド削除は禁止
for (const field of oldFields) {
if (!newFields.has(field)) throw new SchemaCompatibilityError(
`Cannot remove field '${field}' without major version bump`
);
}
}
versions.push({ version, schema, registeredAt: new Date() });
}
validate(eventType, version, data) {
const schema = this.getSchema(eventType, version);
const validate = ajv.compile(schema.schema);
return { valid: validate(data), errors: validate.errors?.map(e => `${e.instancePath} ${e.message}`) };
}
}
// バージョンエンベロープ付きでイベント発行
async publish(eventType, version, data) {
const { valid, errors } = schemaRegistry.validate(eventType, version, data);
if (!valid) throw new SchemaValidationError(errors.join(', '));
await redis.xAdd(`events:${eventType}`, '*', { envelope: JSON.stringify({ id: ulid(), type: eventType, version, payload: data }) });
}
// 受信時: Majorバージョン不一致は処理保留
if (!schemaRegistry.isCompatible(type, receivedVersion, myVersion)) {
logger.warn('Major version mismatch, skipping');
return; // 新しいデプロイを待つ
}
まとめ
- CLAUDE.md にMINOR変更での後方互換ルール(required追加禁止・フィールド削除禁止)・MAJOR変更は破壊的のみ・DLQへの無効イベント送信を明記
- 後方互換チェック をregister時に自動実行——「required追加」「フィールド削除」を試みるとコード時点でエラー
- バージョンエンベロープ でメッセージにtype+versionを含める——受信側が自分の対応バージョンを確認してから処理
- Majorバージョン不一致 は処理を保留——新しいデプロイが完了するまでメッセージを消費しないことで古いコードでの誤処理を防止
アーキテクチャ設計のレビューは **Code Review Pack(¥980)* の /code-review で確認できます。*
みょうが (@myougatheaxo) — ウーパールーパーのVTuber。
Top comments (0)