gRPCやHTTPハンドラにおける構造と責務分離:レビューアーの境界判断
gRPCやHTTPハンドラにおける構造と責務分離:レビューアーの境界判断
gRPCやHTTPハンドラを用いたAPI設計では、リクエスト処理の境界でロジックが集中する傾向がある。
このようなコードをレビューする際、ハンドラの中で完結してよい処理と、分離すべき処理の判断基準が重要になる。
1. ハンドラ肥大化問題の実態
現場ではよく以下のようなコードが見られる:
handlerにロジック集中
func (s *UserServer) RegisterUser(ctx context.Context, req *pb.RegisterRequest) (*pb.RegisterResponse, error) {
if req.Name == "" || !strings.Contains(req.Email, "@") {
return nil, status.Error(codes.InvalidArgument, "invalid input")
}
hashed, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
user := model.User{Name: req.Name, Email: req.Email, PasswordHash: string(hashed)}
if err := s.repo.Save(user); err != nil {
return nil, status.Error(codes.Internal, "failed to save")
}
return &pb.RegisterResponse{Success: true}, nil
}
Comment
@Reviewer: リクエスト検証・ハッシュ処理・DB保存など複数の責務が1関数に詰め込まれている。
ハンドラは"エントリーポイント"に徹し、業務ロジックは別の層に委譲する設計が望ましい。
2. PlantUMLで見る理想構造

Comment
@Reviewer: このような分離構造により、各層の責務が明確化し、テスト容易性と再利用性が向上する。
特に、UseCase層は**Handlerとデータ層の中間**として、変換・検証・ルール処理を担う。
3. ハンドラに詰め込みがちな処理リスト
責務カテゴリ | 本来の位置 | 説明 |
---|---|---|
リクエストバリデーション | Handler | gRPC/HTTP仕様に密接 |
ドメイン制約チェック | UseCase層 | 業務ルールに基づくチェック(例:年齢制限) |
データ変換(DTO → Entity) | UseCase層 | モデルとの整合性確保 |
ビジネスロジック | UseCase層 | 責務単位でテスト可能に |
DBアクセス | Repository | 分離し、インフラ依存を限定化 |
Comment
@Reviewer: バリデーションとドメイン制約は混同されやすい。
APIの形式的チェックと、業務的制約の判断ポイントを明確にする。
4. 改善後の構造:責務ごとの関数委譲
責務分離後のハンドラ
func (s *UserServer) RegisterUser(ctx context.Context, req *pb.RegisterRequest) (*pb.RegisterResponse, error) {
if err := validateRequest(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
input := convertToInput(req)
if err := s.usecase.RegisterUser(ctx, input); err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &pb.RegisterResponse{Success: true}, nil
}
UseCase層での業務処理
func (u *UserUseCase) RegisterUser(ctx context.Context, input RegisterInput) error {
if !strings.Contains(input.Email, "@") {
return errors.New("invalid email format")
}
hashed, err := hashPassword(input.Password)
if err != nil {
return err
}
user := User{Name: input.Name, Email: input.Email, PasswordHash: hashed}
return u.repo.Save(user)
}
Comment
@Reviewer: Handler → UseCase → Repositoryの流れが明確で、各層が責務に集中できる構造に。
この構造なら、UseCase単体テスト・Mock注入が容易で、テスト範囲も明瞭。
5. レビュー観点リスト:Handlerの構造健全性
観点 | 確認ポイント |
---|---|
コントローラの粒度 | 複数の責務を1関数に詰め込んでいないか? |
層の責務 | バリデーション・ドメインチェック・保存処理が正しい層にあるか? |
テスト可能性 | UseCase単体でモックテストできる構造か? |
構造の再利用性 | Handlerから切り出した関数は他でも使えるか? |
非同期処理 | contextのキャンセル・タイムアウト考慮されているか? |
6. その他設計上の注意点
- contextの扱いはUseCaseに委譲しない。キャンセルはHandlerで把握すべき。
- レスポンスの構築はHandlerでまとめる(UseCaseは成功/失敗だけ返す構造)。
- ログ出力も原則Handlerで制御。UseCaseは業務に集中させる。
Comment
@Reviewer: 層の境界が崩れると、テスト困難・変更困難・責任不明瞭という三重苦になる。
ハンドラ層は「受け取って、整えて、渡す」が主な役割。
7. まとめ:責務を正しく見抜くレビュー視点を持つ
gRPC/HTTPのハンドラは、システム外部との接点でありながら、つい実装者の手癖で“全部やってしまう”危険地帯でもある。
レビューアーはこの構造的境界に注目し、以下を問いかけるべきである:
- ハンドラが責務を持ちすぎていないか?
- UseCaseに任せるべき業務ルールが埋もれていないか?
- テストや再利用を阻害する設計になっていないか?
責務の正しい分離は設計の健全性と保守性を支えるレビューの柱であり、コードを読んで終わるのではなく、構造の背後にある意図と癖まで見抜くことが求められる。