1 分

マイクロフレームワークが柔軟なカスタムアーキテクチャを可能にする方法

マイクロフレームワークがモジュール、ミドルウェア、境界を明確にしたカスタムアーキテクチャをチームで組み上げる方法と、トレードオフ、パターン、落とし穴を学びましょう。

マイクロフレームワークが柔軟なカスタムアーキテクチャを可能にする方法

マイクロフレームワークとは何か、そしてなぜ重要か

マイクロフレームワークは、リクエストを受け取り、適切なハンドラへルーティングし、レスポンスを返すという本質に焦点を当てた軽量なウェブフレームワークです。フルスタックフレームワークとは異なり、管理画面、ORM/データベース層、フォームビルダー、バックグラウンドジョブ、認証フローなどをすべて同梱することは通常ありません。代わりに小さく安定したコアを提供し、製品に本当に必要なものだけを追加できるようにします。

マイクロフレームワークとフルスタックフレームワークの比較

フルスタックフレームワークは家具付きの家を買うようなもので、一貫性と便利さがありますが、増改築が難しくなりがちです。マイクロフレームワークは構造的には健全な空の空間に近く、部屋や家具、ユーティリティを自分で決められます。

その自由こそがカスタムアーキテクチャという意味です—チームのニーズ、ドメイン、運用上の制約に合わせてシステム設計を形作ること。平たく言えば、コンポーネント(ロギング、データアクセス、バリデーション、認証、バックグラウンド処理)を自分で選び、それらの接続方法を決めるということです。"ワン・ライト・ウェイ"に従う必要はありません。

なぜチームはマイクロフレームワークを選ぶのか

チームがマイクロフレームワークを選ぶ理由には次が含まれます:

  • 最初のエンドポイントを素早く作る(重いスタックに縛られない)
  • 特定のライブラリを採用/回避する柔軟性
  • サーバーレスやエッジランタイム、最小コンテナなど制約に優しいデプロイ
  • コードベースが成長しても明確な境界を保てること

本記事で扱うこと(と扱わないこと)

ここでは、マイクロフレームワークがどのようにモジュール設計を支援するか、ビルディングブロックの合成、ミドルウェアの利用、依存性注入の導入方法を、プロジェクトを過度に複雑にしない形で説明します。

特定のフレームワークを行ごとに比較したり、マイクロフレームワークが常に優れていると主張したりはしません。目的は構造を意図的に選び、要件の変化に合わせて安全に進化させるための助けとなることです。

コアの考え方:必要なピースだけを組み合わせる

マイクロフレームワークは、アプリケーションをキットのように扱うと最も効果を発揮します。意見の強いスタックを受け入れるのではなく、小さなコアから始め、明確な理由があるときだけ機能を追加します。

最小の有用なコアから始める

実用的な「コア」は通常次の要素だけです:

  • ルーティング:URLをハンドラにマッピングする
  • リクエスト/レスポンス処理:入力を読み取り出力を返す一貫した方法
  • エラーハンドリング:失敗をクリーンなレスポンスに変換する場所

これだけで動作するAPIエンドポイントやウェブページを出荷できます。その他は具体的な理由ができるまでオプションです。

機能は明確なモジュールとして追加する

認証、バリデーション、ロギングが必要になったら、別々のコンポーネントとして追加します—理想的には明確なインタフェースの背後に隠すべきです。これによりアーキテクチャが理解しやすくなります:新しい要素は「どの問題を解決するか?」と「どこに差し込まれるか?」に答えられるべきです。

「必要なものだけ追加する」モジュールの例:

  • 実際のユーザーやサードパーティアクセスがあるときの認証
  • 入力がバグやサポートコストを生み始めたときのバリデーション
  • 本番でのデバッグを素早くするためのロギング/メトリクス

初期の決定は後戻りできるようにする

初期段階では、あなたを閉じ込めない解決策を選びましょう。深いフレームワークの魔法より薄いラッパーや設定を好みます。モジュールを差し替えられるなら、ビジネスロジックを書き直す必要はありません。

アーキテクチャ選択の簡単な完了の定義:チームが各モジュールの目的を説明でき、1〜2日で差し替えられ、個別にテストできること。

組み合わせ可能なアーキテクチャの構成要素

マイクロフレームワークは設計上小規模であり、そのためアプリケーションの「臓器」を選べます。これがカスタムアーキテクチャを実用的にする理由です:最小から始め、実際に必要になったときだけ部品を追加できます。

リクエスト処理:ルータ、ハンドラ、ミドルウェア

多くのマイクロフレームワークベースのアプリは、URLをコントローラ(またはよりシンプルなリクエストハンドラ)にマッピングするルータから始まります。コントローラは機能別(billing、accounts)やインターフェース別(web vs API)に整理できます。

ミドルウェアは通常リクエスト/レスポンスの流れをラップし、横断的関心事の処理に最適です:

  • 認証と認可
  • レート制限
  • キャッシュ
  • メトリクス、ロギング、リクエストトレーシング

ミドルウェアは合成可能なので、グローバルに適用することも(全てにロギングが必要)、特定ルートだけに適用することもできます(管理系エンドポイントはより厳格な認証が必要)。

データアクセス:抽象レベルを選ぶ

マイクロフレームワークはデータ層を強制しないため、チームと負荷に合ったものを選べます:

  • 生産性と一貫したモデリングが欲しければORM
  • 細かい制御が欲しければクエリビルダ
  • パフォーマンスに敏感なクエリや複雑なレポーティングには生SQL

良いパターンはデータアクセスをリポジトリやサービス層の背後に隠すことです。後でツールを切り替えてもハンドラ全体に影響が波及しません。

オプションのモジュール:バックグラウンドジョブとキュー

すべての製品が初日から非同期処理を必要とするわけではありません。必要になったらジョブランナーとキュー(メール送信、映像処理、Webhook)を追加します。バックグラウンドジョブはドメインロジックへの別の「エントリポイント」として扱い、HTTP層と同じサービスを共有してルールを重複させないようにします。

横断的関心事のバックボーンとしてのミドルウェア

ミドルウェアはマイクロフレームワークが最も力を発揮する場所です:すべてのリクエストに共通する処理を各ルートハンドラに増やさずに扱えます。目的は単純です:ハンドラはビジネスロジックに集中させ、配線や周辺処理はミドルウェアで行うこと。

共有処理を上流に移してハンドラを小さく保つ

同じチェックやヘッダを各エンドポイントで繰り返す代わりに1回だけミドルウェアに追加します。クリーンなハンドラは次のように見えます:入力をパースし、サービスを呼び出し、レスポンスを返す。認証、ロギング、バリデーションのデフォルト、レスポンス整形などは前後で行えます。

典型的なミドルウェア順序(とその理由)

順序は振る舞いです。読みやすく一般的なシーケンスは:

  1. Request ID + 基本ログ:相関を早期に作ることで全てのログ行が同一リクエストに紐づきます。
  2. エラーハンドリング/リカバリ:残りを包んで例外を一貫したエラー応答にします。
  3. セキュリティとHTTPヘッダ:CORS、レート制限、認証/セッション解析—実作業の前に。
  4. ボディパース + 入力の正規化:ハンドラが予測可能なデータを受け取れるようにします。
  5. 圧縮:最終的なレスポンスボディを圧縮できるように後半で行います。

圧縮が早すぎるとエラーを見逃す可能性があり、エラーハンドリングが遅すぎるとスタックトレースが漏れたり一貫しない形式を返したりするリスクがあります。

すぐに使える実用例

  • Request IDs: X-Request-Id ヘッダを追加し、ログに含める。
  • エラーハンドリング: 例外を安定したJSON形({ error, message, requestId })にマップする。
  • CORS: 許可するオリジンとヘッダを中央で強制する。
  • 圧縮: JSON中心のAPIでペイロードを削減する。

“ミドルウェアのスパゲッティ”を避ける

ミドルウェアを目的別にグループ化し(観測、セキュリティ、パース、レスポンス整形)、適切なスコープで適用します:本当に普遍的なルールはグローバルに、特定領域(例:/admin)にはルートグループミドルウェアを。各ミドルウェアには明確な名前を付け、セットアップ付近に順序の期待値を短くコメントで残しておくと将来の変更で振る舞いが壊れにくくなります。

IoCとDIを苦痛なく導入する

マイクロフレームワークは薄い「リクエストIN、レスポンスOUT」のコアを与えます。その他すべて(データベース、キャッシュ、メール、サードパーティAPI)は差し替え可能にするべきです。ここで**Inversion of Control(IoC)Dependency Injection(DI)**が役立ちますが、コードベースを大掛かりな実験にしないように注意します。

IoCを簡単に言うと:コードに“買い物”させない

機能がデータベースを必要とすると、その機能の中で直接インスタンスを作るのは誘惑です(“ここで new database client”)。しかしその場所はその特定のDBクライアントに強く結合してしまいます。

IoCは逆のやり方をします:機能は必要なものを要求し、アプリの配線がそれを渡すのです。これにより機能は再利用しやすく、変更も簡単になります。

DI:ハードワイヤーではなく部品を差し込む

依存性注入とは、内部で生成するのではなく外から依存を渡すことを指します。マイクロフレームワークのセットアップでは、起動時に行われることが多いです:

  • 実際のコンポーネント(DB、HTTPクライアント、ロガー)を作る
  • それらをルートハンドラ/サービスに渡す
  • その配線を1か所にまとめる

大きなDIコンテナは不要です。まずは簡単なルールを:依存は1か所で構築し、下方に渡す

実践的アプローチ:ストレージやAPIの周りにインタフェース+アダプタを置く

コンポーネントを差し替え可能にするには、「必要とするもの」を小さなインタフェースで定義し、特定ツールのアダプタを書くとよいです。

例:

  • UserRepository(インタフェース):findById, create, list
  • PostgresUserRepository(アダプタ):Postgresを使った実装
  • InMemoryUserRepository(アダプタ):テスト用実装

ビジネスロジックは UserRepository のことだけを知っていて、Postgresのことは知りません。ストレージの切替は設定上の選択になり、書き換えは不要になります。

同じ考え方は外部APIにも適用できます:

  • PaymentsGateway(インタフェース)
  • StripePaymentsGateway(アダプタ)
  • FakePaymentsGateway(ローカル開発用)

設定は集中化して予測可能に保つ

マイクロフレームワークは設定が各モジュールに散らばりがちです。これを避けましょう。

維持しやすいパターン:

  • 環境変数を一度だけ読む1つの設定モジュール
  • 依存関係グラフを構築する1つのコンポジションルート(起動ファイル)
  • 既に配線されたサービスを受け取るルート

こうすることで、コンポーネントの差し替えは配線レイヤーの小さな変更になります—残りのコードは安定したままです。

マイクロフレームワークがサポートする一般的パターン

ローカルから本番へ移行
配線が整ったらアプリをデプロイしてホストしましょう。

マイクロフレームワークは「唯一の正しい方法」を強制しません。代わりにルーティング、リクエスト/レスポンス処理、少数の拡張ポイントを提供し、チームの規模やプロダクトの成熟度に合ったパターンを採用できます。

レイヤード(Controller / Service / Repository)

馴染みのある「シンプルでクリーン」な構成です:コントローラがHTTP周りを扱い、サービスがビジネスルールを持ち、リポジトリがDBと会話します。

ドメインが単純でチームが小〜中規模、コードの置き場所を予測可能にしたい場合に合います。マイクロフレームワークは自然にこれを支援します:ルートはコントローラにマップされ、コントローラはサービスを呼び、リポジトリは軽量な手動構成で差し込まれます。

ヘキサゴナル(ポートとアダプタ)

ヘキサゴナルアーキテクチャは、今の選択(DB、メッセージバス、外部API、UI)が将来変わることを見越すと有用です。

マイクロフレームワークはここでも相性がよく、"アダプタ"層はHTTPハンドラ+ドメインコマンドへの薄い変換であることが多いです。ポートはドメインのインタフェースで、アダプタがそれを実装します。フレームワークはエッジに留まります。

モジュラー・モノリス(機能モジュールと境界)

運用コストを増やさずにマイクロサービス的な明瞭さを得たい場合、モジュラー・モノリスは有力な選択です。1つのデプロイ単位を保ちながら、機能モジュール(Billing、Accounts、Notificationsなど)に分割し、明示的な公開APIを持たせます。

マイクロフレームワークは自動的にすべてを配線しないため、各モジュールが自分のルート、依存、データアクセスを登録でき、境界が見えやすく、誤って越えにくくなります。

最小の制約で最大の意図を

これら3つのパターンに共通する利点は同じです:ルール(フォルダ構成、依存方向、モジュール境界)を自分で決められること。マイクロフレームワークは小さな安定した接続面を提供し、その上に自由に組み立てられます。

モノリスからマイクロサービスへ:適切な形を選ぶ

マイクロフレームワークは小さく始めて柔軟に保つのを助けますが、システムの“形”をどうするかは別問題です。正しい選択は技術よりもチームサイズ、リリース頻度、調整の痛み方に依存します。

簡単比較:モノリス、モジュラー・モノリス、マイクロサービス

モノリスは1つのデプロイ単位として出荷されます。1つのビルド、1つのログ、1か所でデバッグできるので最速でプロダクトを動かせます。

モジュラー・モノリスは依然として1つのデプロイですが内部が明確に分かれているため、コードベースが大きくなったときの"次の一手"として良い選択です。マイクロフレームワークならモジュールを明示的に保てます。

マイクロサービスはデプロイ単位を分割します。チーム間の結合を減らせますが、運用作業が増えます。

サービス境界:いつ分割するか(いつ分割しないか)

境界がすでに現実にあるときに分割すべきです:

  • 異なるチームが独立してリリースする必要があるとき
  • モジュールが明確に異なるデータ所有とルールを持っているとき(単なる別エンドポイントではない)
  • スケーリング要件が本当に異なるとき(例:重いバックグラウンド処理 vs 軽量な読み取り)

フォルダが大きいというだけの理由や、サービスが同じDBテーブルを共有するような場合は分割を避けるべきです。それは安定した境界が見つかっていないサインです。

APIゲートウェイと共有ライブラリ:利点と欠点

APIゲートウェイはクライアントを簡素化できます(エントリを1つにし、認証やレート制限を集中化)。しかし、賢くなりすぎるとボトルネックや単一障害点になるリスクがあります。

共有ライブラリは開発を高速化しますが、隠れた結合も生みます。複数サービスが同時にアップグレードを強いられるなら、分散モノリスを再現してしまいます。

計画すべき運用上の負担

マイクロサービスは継続的なコストを増やします:追加のデプロイパイプライン、バージョニング、サービスディスカバリ、モニタリング、トレーシング、インシデント対応、オンコールローテーションなど。チームがその運用を快適に回せないなら、マイクロフレームワーク部品で作ったモジュラー・モノリスの方が安全です。

実用的設計図:保守しやすいマイクロフレームワーク構成

マイクロフレームワークは自由を与えますが、保守性は設計しなければなりません。目標は「カスタム」の部分が見つけやすく、差し替えやすく、誤用しにくいことです。

1) シンプルで退屈なプロジェクト構成から始める

1分で説明でき、コードレビューで守れる構成を選びます。一つの実用的な分割例:

  • app/(コンポジションルート:モジュールを配線)
  • modules/(ビジネス機能)
  • transport/(HTTPルーティング、リクエスト/レスポンスのマッピング)
  • shared/(共通ユーティリティ:設定、ロギング、エラー型)
  • tests/

命名を一貫させます:モジュールフォルダは名詞(billing, users)、エントリポイントは予測可能に(index, routes, service)。

2) モジュール所有権と公開APIを定義する

各モジュールを小さなプロダクトのように扱います:

  • 小さな公開サーフェスを公開(例:modules/users/public.ts
  • 内部は非公開に保つ(modules/users/internal/*
  • 所有権(誰がここを承認するか)や期待値(SLA、データルール)を文書化する

modules/orders/internal/db.ts のような“貫通”インポートを別モジュールから行うのは避けてください。もし別モジュールが必要なら、公開APIに昇格させます。

3) 初日から観測性を組み込む

小さなサービスでも基本的な可視性は必要です:

  • リクエストID付きの構造化ログ
  • 最低限のメトリクスセット(レイテンシ、エラー率、主要なビジネスカウンタ)
  • 外部呼び出し(HTTP、DB、キュー)に対するトレーシングフック

これらを shared/observability に置き、すべてのルートハンドラが同じ慣習を使うようにします。

4) バリデーションとエラー応答を標準化する

クライアントにとって予測可能で、人的にデバッグしやすいエラーを作ります。共通のエラー形(code, message, details, requestId など)と各エンドポイントごとのスキーマを定義します。内部例外からHTTPレスポンスへのマッピングは中央に集約しておき、ハンドラはビジネスロジックに集中できるようにします。

Koder.aiの位置づけ(速度を上げつつロックインを避けたいとき)

素早く動きつつマイクロフレームワーク的なアーキテクチャを明示的に保ちたい場合、Koder.ai はスキャフォールディングと反復ツールとして有用です。置き換えではなく基盤を素早く作る手助けになります。モジュール境界、ミドルウェアスタック、エラーフォーマットをチャットで説明して、動作するベースアプリ(例:Reactフロント+Go + PostgreSQLバックエンド)を生成し、その配線を意図的に洗練していけます。

特にアーキテクチャ作業に合う2つの機能:

  • Planning mode:生成や変更の前に構造(モジュール、ポート/アダプタ、ルートグループ)を揃える
  • Snapshots and rollback:DI導入、モジュール抽出、キュー追加などの実験を安全に行い戻せる

Koder.aiはソースコードのエクスポートをサポートするため、生成物はリポジトリで所有し、手作りプロジェクトと同じように進化させられます。

カスタムアーキテクチャを安全に保つテスト戦略

フルスタックを素早くプロトタイプ
ファイルを手作業で組まずにReactフロントエンドとバックエンド接続を立ち上げます。

マイクロフレームワークベースのシステムは“手組み”に見えることが多く、テストはフレームワークの慣習よりも部品間の継ぎ目を守ることにフォーカスすべきです。目的はフルエンドツーエンドを毎回走らせることなく自信を持つことです。

ユニット vs 統合:優先順位

まずビジネスルール(バリデーション、料金計算、権限ロジック)のユニットテストを行ってください。これが速く障害箇所を特定できます。

次に価値が高い少数の統合テストを投資します:配線(ルーティング→ミドルウェア→ハンドラ→永続化境界)を検証するテストです。これらはコンポーネントが合わさったときに起きる微妙なバグを捕まえます。

ミドルウェアとリクエストハンドラの効果的なテスト

ミドルウェアは横断的な振る舞い(認証、ロギング、レート制限)を隠します。パイプラインとしてテストしてください:

  • 最小限のリクエストコンテキストを作る
  • 次のハンドラを記録する「next handler」を使ってミドルウェアを実行する
  • 副作用(ヘッダ追加など)と制御フロー(認証がない場合にブロックされること)をアサートする

ハンドラは内部呼び出しではなく公開されるHTTP形(ステータスコード、ヘッダ、レスポンスボディ)をテストすることを好みます。こうすることで内部が変わってもテストは安定します。

DIやフェイクで外部サービスを分離する

依存注入や単純なコンストラクタ引数を使って実際の依存をフェイクに差し替えます:

  • ネットワーク呼び出しを避けるためのフェイクメール/ペイメントクライアント
  • ユニットテスト用のインメモリリポジトリ
  • クエリを検証するためのローカルDBコンテナを使った少数の統合テスト

チームが増えたら契約テストを追加する

複数サービスやチームがAPIに依存する場合、契約テストを追加して期待されるリクエスト/レスポンスを固定化します。プロバイダ側の契約テストは、マイクロフレームワークの内部やモジュールが進化してもコンシューマを壊さないことを保証します。

注意すべきトレードオフと落とし穴

マイクロフレームワークは自由を与えますが、自由は必ずしも明瞭さを生むわけではありません。チームが増えコードベースが拡大すると、「一時的」な決定が恒久化するリスクが出ます。

柔軟性が不整合に変わることがある

組み込みの慣習が少ないと、2つのチームが同じ機能を異なるスタイル(ルーティング、エラーハンドリング、レスポンス形式、ロギング)で実装してしまいがちです。不整合はレビューを遅くし、オンボーディングを難しくします。

簡単なガードレールが有効です:プロジェクト構成、命名、エラーフォーマット、ロギングフィールドを記した「サービステンプレート」ドキュメントを用意し、スターターリポジトリと数個のリンティングルールで強制します。

隠れた結合と shared-utils の肥大化

プロジェクトは最初は綺麗でも、utils/ フォルダが徐々に第2のフレームワークになっていくことがあります。モジュールがヘルパーや定数、グローバル状態を共有すると境界がぼやけ、変更が驚くような破壊を生みます。

明示的な共有パッケージ(バージョニング付き)を好むか、共有は最小限に留めます:型、インタフェース、よくテストされたプリミティブのみ。もしヘルパーがビジネスルールに依存するなら、それはドメインモジュールへ属すべきです。

認証やバリデーションを自分で組むとセキュリティギャップが生まれやすい

認証、認可、入力検証、レート制限を手動で配線すると、ルートを見落としたりミドルウェアを忘れたり、ハッピーパスだけを検証してしまったりしがちです。

セキュリティのデフォルトを中央化してください:セキュアなヘッダ、一貫した認可チェック、エッジでのバリデーション。保護されたエンドポイントが確かに保護されていることをアサートするテストを追加しましょう。

ミドルウェアチェーンはパフォーマンスに悪影響を与えることがある

無計画なミドルウェアの積み重ねはオーバーヘッドを招きます—特に複数のミドルウェアがボディをパースしたり、ストレージにアクセスしたり、ログをシリアライズしたりする場合。

ミドルウェアは小さく測定可能に保ちます。標準順序を文書化し、新しいミドルウェアを追加する際はコストをレビューします。ボトルネックが疑われるときはリクエストをプロファイルして冗長なステップを取り除きます。

アーキテクチャ選択のための簡単な意思決定ガイド

最初のエンドポイントを公開
明確なモジュールと一貫したエラー形でGo+PostgreSQLバックエンドを作成します。

マイクロフレームワークは選択肢を与えますが、選択にはプロセスが必要です。目標は「最高のアーキテクチャ」を見つけることではなく、チームが構築・運用・変更できる形を選ぶことです。

ステップ1:簡単なチェックリストを実行する

モノリスかマイクロサービスかを選ぶ前に次に答えてください:

  • チーム規模とスキル:複数のデプロイ可能なサービスを現実的に運用できるか(オンコール、CI/CD、観測性),それとも単一のシンプルなランタイムが必要か?
  • 納期:スピードが最優先なら、可動部品は少なく始め分割は後回しに。
  • コンプライアンスとセキュリティ要件:監査、データ居住性、アクセス制御は構造を技術より強く決めることがある。

不確かな場合は、マイクロフレームワークで構築したモジュラー・モノリスをデフォルトにしてください。境界を明確に保ちながら出荷は容易です。

ステップ2:早めに慣習を決め(書き残し)

マイクロフレームワークは一貫性を強制しないので、慣習を前もって決めて書き残します:

  • ルーティングスタイル(RESTful資源 vs アクションルート)
  • フォルダレイアウト(機能別 vs 技術層別)
  • 一貫したエラーフォーマット(クライアントが失敗を扱いやすくするため)

/docs に1ページの「サービス契約」を置くだけでも十分なことが多いです。

ステップ3:必須モジュールを決める

最初からほとんどのエンドポイントで必要になる横断モジュールを用意します:

  • 認証/認可
  • ロギングとリクエストトレーシング
  • 入力バリデーションとエラーハンドリング

これらは共有モジュールとして扱い、コピー&ペーストではなく再利用できるようにします。

ステップ4:四半期ごとに再評価する

アーキテクチャは要件に応じて変わるべきです。毎四半期、デプロイが遅くなっている箇所、スケール要件が異なる箇所、最も壊れる部分をレビューします。あるドメインがボトルネックになっているなら、それが次に分割すべき候補です—システム全体ではありません。

例:カスタムアーキテクチャがどのように成長するか

マイクロフレームワークのセットアップは通常「完璧に設計されている」わけではありません。1つのAPI、1つのチーム、タイトな納期で始まることが多く、価値は製品が成長するにつれて現れます:機能が増え、より多くの人がコードに触り、アーキテクチャは伸び縮みする必要があります。

ステージ1:少数のルートを持つ単一API

最小のサービス(ルーティング、リクエストパース、1つのDBアダプタ)で始めます。多くのロジックはエンドポイント近くにありますが、それは出荷速度を優先したためです。

ステージ2:機能がモジュール化される

認証、支払い、通知、レポーティングを追加するにつれ、それらをモジュール(フォルダやパッケージ)に分離します。各モジュールは自分のモデル、ビジネスルール、データアクセスを所有し、外部に必要なものだけを公開します。

ステージ3:横断的関心事をミドルウェアへ移す

ロギング、認可チェック、レート制限、リクエストバリデーションはミドルウェアに移され、すべてのエンドポイントが一貫した振る舞いになります。順序が重要なので文書化しておくべきです。

成長を予測可能に保つために何を文書化すべきか

文書化すること:

  • モジュール境界:各モジュールが所有するもの、触れてはいけないもの
  • ミドルウェア順序:何が先に動き、何が最後に動くか、なぜか
  • SLA/期待値:レイテンシ目標、エラーバジェット、依存契約(最初は非公式でもよい)

リファクタや分割の合図

モジュールが内部を多く共有し始める、ビルド時間が目に見えて遅くなる、あるいは小さな変更が複数モジュールにまたがる必要が出てきたらリファクタのサインです。

チームが共有デプロイでブロックされる、異なる部分が異なるスケールを必要とする、あるいは統合境界が既に別プロダクトのように振る舞っているならサービス分割を検討します。

結論と次のステップ

マイクロフレームワークは、所与のスタックに従うのではなくドメインに合わせてアプリケーションを形作りたい場合に適しています。明確さを利便性より重視するチーム—つまり、いくつかの重要なビルディングブロックを選び(そして維持し)ることを厭わないチーム—に特に向いています。

軌道に乗せるための重要なポイント

柔軟性を活かすために守るべき習慣:

  • 境界を優先する:何が一緒に属するか、何が境界を越えてはならないかを決める。
  • モジュールを魔法より優先する:小さく明示的なコンポーネントを好み、共有状態は最小にする。
  • 一貫性は創造性に勝る:リクエスト/レスポンス形、エラーハンドリング、ロギングフィールド、設定パターンを標準化する。
  • テストが安全網になる:アーキテクチャをカスタマイズしたら、リファクタを安全にするのはテストです。

今週できる次のステップ

2つの軽量アーティファクトから始めてください:

  1. モジュールマップを作る:コアモジュール、公開インタフェース、許容する依存関係を列挙する。
  2. ミドルウェアスタックを作る:各ミドルウェアの順序と目的(認証、バリデーション、レート制限、トレーシング、エラーハンドリング)を書き、そのミドルウェアがリクエストコンテキストに追加してよいデータを定義する。

最後に、決定を行ったら文書化してください—短いメモでも役に立ちます。リポジトリに「Architecture Decisions」ページを置き、定期的に見直して昨日の近道が今日の制約にならないようにしましょう。

よくある質問

What is a microframework, and how is it different from a full-stack framework?

マイクロフレームワークは本質的な部分に集中します:ルーティング、リクエスト/レスポンス処理、基本的な拡張ポイント。

フルスタックフレームワークは通常、多くの「バッテリー同梱」機能(ORM、認証、管理画面、フォーム、バックグラウンドジョブなど)を含みます。マイクロフレームワークは利便性を犠牲にしてまで制御を提供します—必要なものだけを追加し、各パーツの接続方法を自分たちで決めます。

When should a team choose a microframework?

マイクロフレームワークは以下のような場合に適しています:

  • 重厚な意見を持つスタックに縛られず、素早く公開したいとき
  • サーバーレスやエッジ、最小コンテナなど制約のある環境で動かすとき
  • コードベースが成長してもサービスやモジュールの境界を明確に保ちたいとき
  • DB層や認証、キューなどのコンポーネントを最小限の影響で入れ替えたいとき
What’s the minimum set of pieces you need to start a microframework app?

「最小の有用なコア」は通常次のとおりです:

  • ルーティング(URL → ハンドラ)
  • リクエスト/レスポンスのプリミティブ(入力の読み取り、出力の返却)
  • 集中化されたエラーハンドリング(一貫した失敗処理)

まずはそこから始め、1つのエンドポイントを出荷してから、明確な価値があるときにモジュール(認証、バリデーション、観測性、キューなど)を追加します。

What belongs in middleware vs. in route handlers?

ミドルウェアは広く適用される横断的関心事に最適です。例:

  • リクエストIDと構造化ログ
  • エラーハンドリング/リカバリ
  • セキュリティヘッダ、CORS、レート制限
  • 認証/セッションの解析
  • ボディのパースと正規化
  • 圧縮

ルートハンドラはビジネスロジックに集中させます:パース → サービス呼び出し → レスポンス返却。

What is a sensible middleware order for APIs?

オーダーは振る舞いを決めます。一般的で信頼できる順序は:

  1. Request ID + 基本的なログ(早期に相関を作る)
  2. エラーハンドリング/リカバリ(残りを包んで例外を一貫したレスポンスに)
  3. セキュリティヘッダ + CORS + レート制限 + 認証
  4. ボディパース + バリデーション/入力の正規化
  5. 圧縮(最後のほう)

セットアップ付近に順序を文書化しておくと、将来の変更でレスポンスやセキュリティが壊れるのを防げます。

What does Inversion of Control (IoC) mean in a microframework project?

Inversion of Control は、ビジネスコードが自分で依存関係を作らない(“買いに行かない”)ことを意味します。代わりに、アプリの組み立て側が必要なものを渡します。

実務的には:起動時にデータベースクライアント、ロガー、APIクライアントを構築し、それらをサービス/ハンドラへ渡します。これにより強い結合が減り、テストや実装の差し替えが容易になります。

Do you need a DI container when using a microframework?

いいえ。多くのDI利点はシンプルなコンポジションルートで得られます:

  • 依存関係を一度だけ作成する(DB、ロガー、HTTPクライアント)
  • モジュール工場/サービスに渡す
  • 配線を1つの予測可能なファイル/モジュールにまとめる

依存関係グラフの管理が手作業で辛くなったらコンテナを検討しても遅くはありません—最初から複雑さを導入しないでください。

How do you keep components swappable (e.g., database or payments)?

ストレージや外部APIを小さなインタフェース(ポート)の背後に置き、アダプターを実装します:

  • UserRepository(インタフェース): findById, create, list
  • PostgresUserRepository(アダプタ): 本番用のPostgres実装
  • InMemoryUserRepository(アダプタ): テスト用

ハンドラやサービスはインタフェースに依存し、具体実装には依存しません。データベースや外部プロバイダの切替は配線/設定変更だけで済みます。

What project structure helps microframework apps stay maintainable?

見通しの良い境界を保つための実用的な構成例:

  • app/(コンポジションルート:モジュールを結線)
  • modules/(機能モジュール:ドメイン能力)
  • transport/(HTTPルーティング、リクエスト/レスポンスのマッピング)
  • shared/(設定、ロギング、エラー型、観測性)
  • tests/

モジュールの公開API(例:modules/users/public.ts)を定め、内部への“貫通インポート”は避けます。

What testing approach works best for custom microframework architectures?

ビジネスルール(バリデーション、価格計算、権限ロジック)のユニットテストを優先し、次に高価値の統合テストを少数用意して配線(ルーティング→ミドルウェア→ハンドラ→永続化境界)を検証します。

DI/フェイクを使って外部サービスを分離し、ミドルウェアはパイプラインとしてテストします。チームが増える場合は、APIの契約テストも導入して破壊的変更を防ぎます。

Related posts