バーティカルスライスアーキテクチャ

英語名 Vertical Slice Architecture
読み方 バーティカル スライス アーキテクチャ
難易度
所要時間 30分〜1時間
提唱者 Jimmy Bogard (2018)
目次

ひとことで言うと
#

従来のレイヤードアーキテクチャ(Controller → Service → Repository)を横に切る代わりに、機能(ユースケース)単位で全層を縦に切り、各スライスが自己完結する設計手法。

押さえておきたい用語
#

押さえておきたい用語
Vertical Slice(バーティカルスライス)
1つのユースケースに必要なUIからDBまでの全レイヤーを含む縦の単位のこと。スライスごとに独立して開発・テストできる。
Horizontal Layer(ホリゾンタルレイヤー)
Controller・Service・Repositoryのように技術的関心で分割した横の層を指す。従来のレイヤードアーキテクチャの基本構造。
Handler(ハンドラー)
1つのリクエストを受け取り処理を完結させるスライスの中核コンポーネントである。MediatRなどのライブラリでディスパッチされる。
Cross-Cutting Concern(横断的関心事)
認証・ログ・バリデーションなど複数のスライスに共通する処理。パイプライン(ミドルウェア)として実装し、スライスの外に置く。

バーティカルスライスアーキテクチャの全体像
#

バーティカルスライス:レイヤーを横ではなく縦に切る
APILogicData各スライスが全層を含むCreateOrderEndpointHandler + ValidatorBusiness RulesDB Write (SQL)自己完結GetOrderListEndpointHandler + MapperProjection LogicDB Read (Dapper)自己完結CancelOrderEndpointHandler + DomainCancellation RulesDB Write + Event自己完結横断的関心事(Pipeline / Middleware)認証 / ログ / バリデーション / トランザクション
バーティカルスライス導入の進め方フロー
1
ユースケース特定
機能一覧からスライスの単位を決める
2
Handler実装
1スライス=1Handlerで全層を実装
3
横断処理の分離
共通処理をPipelineに抽出
独立テスト
スライス単位で完結するテストを整備

こんな悩みに効く
#

  • Serviceクラスが肥大化して1ファイル1,000行を超えている
  • ある機能の修正が、関係ない機能のテストを壊す
  • 新機能追加のたびにRepository・Service・Controllerの3層すべてに手を入れる必要がある

基本の使い方
#

ユースケース(コマンド/クエリ)を単位としてスライスを定義する
「注文を作成する」「注文一覧を取得する」「注文をキャンセルする」のように、1つのAPIエンドポイント=1スライスとして定義する。CQRSの考え方と自然に親和する。
各スライスをHandlerとして自己完結的に実装する
スライスごとにRequest/Response型、Handler、必要なDB操作を1つのファイル(またはフォルダ)にまとめる。共有のServiceやRepositoryクラスは原則作らない。
横断的関心事はPipelineに分離する
認証、ログ、バリデーション、トランザクション管理など、複数スライスに共通する処理はMediatRのPipeline BehaviorやASP.NETのMiddlewareとして実装する。スライス内には含めない。

具体例
#

例1:ECサイトが注文ドメインのService肥大化を解消する

エンジニア25名のEC企業。OrderService2,800行 に膨れ上がり、注文作成・一覧取得・キャンセル・返品・配送状況更新の全ロジックが1クラスに集約されていた。1つの機能修正でOrderService全体のテスト(340件)が走り、CIに 18分 かかっていた。

バーティカルスライスに移行し、CreateOrderGetOrderListCancelOrder等の各Handlerに分割。各スライスは自分に必要なDB操作を直接持ち、共有Repositoryは廃止した。

スライス単位のテスト実行時間は平均 45秒。機能修正時に影響する他スライスが ゼロ になり、エンジニアのコンフリクト解消作業が月 20時間 → 2時間 に減少した。

例2:金融系SaaSがCQRSへの段階移行の足がかりにする

エンジニア40名の金融系SaaS。Read/Write比率が 20:1 と読み取りが支配的だったが、レイヤードアーキテクチャでは読み取りも書き込みも同じService → Repositoryを経由しており、Readの最適化が困難だった。

まずバーティカルスライスに移行し、Command(Write)とQuery(Read)を別スライスに分離。QueryスライスはORMを使わずDapperで直接SQLを発行する形に変更。Commandスライスは既存のドメインモデルを活用した。

Read系のレスポンスタイムが 320ms → 45ms に改善。その後、Read側のみ別DBに向ける本格的なCQRS移行が 3週間 で完了している。

例3:スタートアップが少人数で高速に機能開発を並列化する

エンジニア6名のスタートアップ。レイヤードアーキテクチャでServiceクラスへの変更がコンフリクトの温床になり、3名が同時に作業すると日に 4〜5回 のマージコンフリクトが発生。機能開発の並列性が事実上 2名 に制限されていた。

バーティカルスライスを採用し、1機能=1フォルダ(Handler + Request/Response + テスト)の構成に変更。各エンジニアが異なるスライスを担当し、ファイルレベルでの衝突をほぼゼロにした。

マージコンフリクトが日 0.3回 に激減。6名全員の並列開発が可能になり、スプリントあたりの完了ストーリーポイントが 42 → 68 に向上した。

やりがちな失敗パターン
#

  1. スライス間でコードを共有しすぎる — 共通化を急ぐとスライスの独立性が失われる。重複を許容し、3回以上繰り返されたら初めて共通化を検討する
  2. スライスの粒度が大きすぎる — 「注文管理」全体を1スライスにすると結局Serviceの肥大化と同じ。1エンドポイント=1スライスが原則
  3. 横断的関心事をスライス内に書く — 認証やログを各Handlerに書くと重複が爆発する。Pipelineに分離する
  4. 既存コードを一括で移行しようとする — 新機能からスライス方式で書き始め、既存コードは触る際に段階的に移行する

まとめ
#

バーティカルスライスアーキテクチャは、レイヤードアーキテクチャの横分割に代わり、ユースケース単位で全層を縦に切る設計手法。各スライスが自己完結することでService肥大化・テスト結合・コンフリクトの問題を解消する。横断的関心事はPipelineに分離し、CQRSやModular Monolithとの組み合わせで段階的に発展させることができる。