C++17例外透過設計レビュー|透過設計の明示とレビューアーが確認すべき責務分界の技術
この記事のポイント
- 例外透過設計(Exception Transparency)の設計意図をレビューアーが読み取る技術を整理
- 伝播責務とハンドリング責務の分離判断をレビューで具体指摘可能にする
- 透過設計を採るべき場面・採らないべき場面の線引きをレビュー観点で学習する
そもそも例外透過設計とは
例外透過(exception transparency) とは:
- 自層では例外を握り潰さず、上位層に伝播させる設計方針
- 例外を契約の一部として公開する
- 層単位の責務整理を容易化する代わりに、ハンドリング責務は上位へ委譲
[発生層] → [中間層(透過)] → [ハンドリング層(収束)]なぜこれをレビューするのか
- 握り潰し設計を抑止する役割
- 層越境した副作用干渉を排除できるか確認
- ハンドリング責務と通知責務を混同していないか確認
- 全体契約として伝播経路を明示的に管理するため
透過設計は責務分界のレビューで極めて重要な論点になります。
レビューアー視点
- この層でハンドリングすべき責務を持つのか?
- 復旧可能性が本当に存在しないのか?
- 透過設計方針がプロジェクトで統一されているか?
- API契約として例外伝播を正しく文書化しているか?
- ログや監視責務は別途持たれているか?
開発者視点
- 原則「復旧不能なら透過する」
- 業務層が復旧責務を持つ範囲を限定する
- 透過契約に依存しすぎず必要な文脈付加は実施する
- API契約上の明示責務整理を意識する
良い実装例
完全透過設計
完全透過型の中間層
#include <stdexcept>
class Repository {
public:
void save() {
throw std::runtime_error("DB failure");
}
};
class Service {
public:
void process() {
repository_.save(); // 透過的に上位に伝播
}
private:
Repository repository_;
};- Service層ではハンドリング責務を持たず透過
- Application層が最終捕捉
文脈付加透過設計
文脈情報を付加して透過
class Service {
public:
void process() {
try {
repository_.save();
} catch (const std::exception& e) {
throw std::runtime_error(std::string("Service::process failed: ") + e.what());
}
}
};- 原因情報保持+責務層情報付加
レビュー観点
- 業務責務層がハンドリングを持たず透過設計になっているか
- 握り潰し(catch …)が中間層に混在していないか
- 文脈情報の付加は実施されているか
- API契約ドキュメントに透過例外が明示されているか
- 上位層catch戦略が整理されているか
良くない実装例: ケース1(中間層握り潰し)
中間層握り潰し型
class Service {
public:
void process() {
try {
repository_.save();
} catch (...) {
@Reviewer中間層でのcatch(...)は障害調査困難化を招きます。透過設計方針に統一し、上位に伝播させてください。 }
}
};改善例
改善例(透過設計統一)
class Service {
public:
void process() {
repository_.save();
}
};良くない実装例: ケース2(上位層伝播整理不足)
上位層catch設計放棄
void runApplication() {
service.process(); // 透過されてきた例外を無視
@Reviewer透過設計を採るなら、上位層でのcatch網羅設計が必要です。復旧・通知・監視を統合してください。}改善例
改善例(収束catch統合)
void runApplication() {
try {
service.process();
} catch (const std::exception& e) {
logger.error("Application failure: {}", e.what());
notifyMonitoring(e);
}
}良くない実装例: ケース3(透過契約破綻)
API契約矛盾例
class Repository {
public:
void save(); // 仕様上「例外は投げません」とAPI契約に明記
};%% @Reviewer 例外透過設計を採用しているならAPI契約上もthrows可能性を正確に文書化してください。契約整合性が失われています。
改善例
改善例(透過契約明示)
class Repository {
public:
void save(); // throws std::runtime_error
};PlantUML:透過設計責務フロー
ロギング戦略統合例
透過+収束設計
try {
service.process();
} catch (const LogicException& e) {
logger.warn("Validation failure: {}", e.what());
} catch (const RuntimeException& e) {
logger.error("Runtime failure: {}", e.what());
notifyOps(e);
} catch (const std::exception& e) {
logger.fatal("Unhandled failure: {}", e.what());
}- 透過は収束先のcatch網羅設計とセットで成立
観点チェックリスト
まとめ
レビューアーが透過設計レビューで問うべきは
「この層が例外を止める責任を本当に持つべきか?」
です。
- 中間層透過 → 責務境界整理
- 収束層捕捉 → ログ・通知統合
透過設計レビューは「握り潰し文化を作らせない防御壁」です。
レビュー現場では「この層のcatchは不要です、透過統一しましょう」と
責務分界を明示指摘するレビュー文化が品質を高めます。
