nestjs-oop-design-patterns について
NestJS プロジェクト向けのオブジェクト指向設計スキルです。実際の課題に基づいて責務、パターン、安全なリファクタリングを検討し、抽象化を加える前に既存構造を確認します。
主な機能
- 01オブジェクトの役割と設計上の課題を確認
- 02SOLID に基づく設計の評価
- 03実際の変化に応じたパターン選択
- 04振る舞いを保つリファクタリングの計画
- 05変更境界に応じたテストの設計
- 06指示の衝突と前提条件の確認
活用シーン
- 01NestJS プロバイダーの責務整理
- 02実際に変化するアルゴリズムへのストラテジー選択
- 03ベンダーモデルのアプリケーションコードからの分離
- 04リファクタリング前の既存動作の固定
- 05デザインパターンによる複雑さの評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ amirtaherkhani/ nestjs-skills --skill nestjs-oop-design-patternsインストール後、Claude Code で/nestjs-oop-design-patternsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/nestjs-native-patterns.mdobject-design.mdoop-solid.mdpattern-catalog.mdsmells-refactoring.md詳細
NestJS の OOP とデザインパターン
明確な責務、カプセル化された不変条件、意図的な連携を通じて、変更容易性と正確性を高めます。パターンは、実際に確認された課題を解決するために使い、飾りとして使わないでください。
実行前の競合ガード
ファイルを編集したり、状態を変更するコマンドを実行したりする前に、有効な指示を照合してください。読み取り専用の調査は続けてもかまいません。
前提条件
リポジトリの指示、インストール済みのバージョン、呼び出し元、テスト、モジュールの配線、実際に確認された設計上の課題を調査してください。振る舞いとユーザーの変更を保持し、アーキテクチャや実行時の制約が判明する前にパターンを選ばないでください。ほかの有効なスキルを読むのは、その判断が重複する場合に限ります。
主な担当範囲
このスキルの担当範囲は、オブジェクトの責務、不変条件、カプセル化、コラボレーターの契約、SOLID のトレードオフ、パターン、振る舞いを維持するリファクタリングです。インストール済みで関連する場合は、以下と連携してください。
nestjs-architecture-principles:モジュール、依存関係、データ、トランザクションの境界。nestjs-features-performance:ライフサイクル、API/セキュリティ、テスト、実行時、パフォーマンス。nestjs-professional-software-engineering:実装と検証。nestjs-code-audit:読み取り専用の品質根拠と重複を排除した報告。nestjs-feature-audit:ブランチ固有のロードマップ上のゲートと機能の報告。nestjs-git-commit-pr-message:承認された Git の公開と CI のフォローアップ。
これらは判断の境界であり、必須の依存関係ではありません。作業を別のワークフローに引き継ぐ場合も、ドメインの担当範囲は維持してください。
競合の確認
編集内容や契約、コマンドの順序や副作用、担当者の競合、バージョン・根拠・承認・リポジトリに関する前提条件の未充足を確認してください。明示されたユーザーの意図、リポジトリの契約、検証済みの実行時制約、そして最も範囲の狭い担当者の順に解決してください。各判断の主担当は一人にし、相容れない助言を満たすために並行して設計を作らないでください。重要な競合が残る場合は、変更を加える前に作業を止め、必要最小限の判断を求めてください。監査は読み取り専用とし、明示的な修正許可は独立した実装フェーズで適用します。
提案する前に調査する
- 関連するコントローラー/プロバイダー/エンティティと、その呼び出し元、テスト、モジュールの配線、DTO、永続化マッピングを読みます。
- オブジェクトの役割を特定します。トランスポートアダプター、アプリケーションコーディネーター、ドメインオブジェクト/ポリシー、インフラストラクチャアダプター、コンポジションルートのいずれかです。
- 実際の問題を明確にします。不変条件の漏れ、変更時の不整合、置き換えの難しさ、隠れた依存関係、判断ロジックの重複、フレームワークへの結合などです。
- 新しい抽象化を追加する前に、リポジトリの規約と既存の抽象化を確認します。
- リスクの高いリファクタリングの前に、特性テストで振る舞いを保持します。
ファイルの長さだけで設計が悪いと判断しないでください。複雑さ、凝集度、公開範囲、依存関係の数、変更履歴のほうが、より確かな根拠になります。
OOP の基本方針
- 不変条件をカプセル化します。ゲッターやセッターの背後にデータを移すだけでは不十分です。
- 継承よりもコンポジションと委譲を優先します。
- 可変状態は非公開にし、その存続期間を最小限に抑えます。
- 安定した操作に実質的に異なる実装がある場合は、ポリモーフィズムを使います。
- 副作用と変動性は注入し、決定論的な計算はプレーンなままにします。
- ドメイン上の価値がある場合は、無効な状態を構築しにくくする型を使います。
- 型、メソッド、テスト、エラーの間でドメイン用語を一貫して使います。
- プロバイダーは Nest のコンテナーに構築させます。ビジネスメソッドの中で依存関係グラフをインスタンス化しないでください。
詳しい OOP と SOLID の指針については references/oop-solid.md を、エンティティ、値オブジェクト、ポリシー、サービス、DTO の境界については references/object-design.md を参照してください。
診断の問いとして SOLID を適用する
単一責任
この単位は、まとまりのある1つの方針または変更軸を担っていますか? ユースケースは複数の協力者を調整していても、責任は1つである場合があります。すべてのメソッドをプロバイダーに分割すると、まとまりが生まれないまま、コードを追う手間が増えます。
開放/閉鎖
同じ条件分岐を繰り返し変更させる、実際のバリエーションがありますか? そのバリエーションを囲むストラテジー、ポリシー、またはアダプターを抽出してください。仮定上のプロバイダーに対応するための拡張ポイントは作らないでください。
リスコフの置換
すべての実装が、同じ入力、出力、エラー、副作用、タイミングの前提、そして不変条件を守れますか? そうでない場合は、契約を限定するか、継承ではなくコンポジションを使用してください。
インターフェース分離
各利用側は、使用する機能だけに依存していますか? 1つの CommonService や汎用リポジトリーインターフェースよりも、アプリケーションが所有する小さなポートを優先してください。
依存性逆転
Nest モジュールがインフラストラクチャ実装を選択する一方で、高水準のポリシーはアプリケーションの契約に依存していますか? 変化しやすい副作用には境界を設けますが、すべてのローカルクラスにインターフェースを用意する必要はありません。
力学に基づいてパターンを選択する
references/pattern-catalog.md および references/nestjs-native-patterns.md を参照してください。
パターンカタログは、パターンを見つけるための参考資料として扱い、実装チェックリストとしては扱わないでください。アーキテクチャスタイル、フレームワークのライフサイクル機構、プロバイダーのライフタイム、GoF のオブジェクトパターンは、記事でひとまとめにされていても、それぞれ異なる種類の問題を解決します。人気があること、なじみがあること、あるいは「スケーラブル」だという主張は、現在のリポジトリーにそのパターンが必要であることの根拠にはなりません。
| シグナル | 検討するもの | 避ける状況 |
|---|---|---|
| 同じ操作に対して、実際に差し替え可能な複数のアルゴリズムがある | ストラテジー | アルゴリズムが安定している |
| 構成や型によって生成方法が異なる | ファクトリー | コンストラクターがすでにシンプルである |
| 複雑な生成に、順序がある、または任意で検証済みの手順が必要である | ビルダー | オブジェクトリテラルやコンストラクターで明快に表現できる |
| ベンダーのモデルがアプリケーションコードに漏れている | アダプター | ライブラリー API が境界で既に分離されている |
| 抽象と実装に、独立した2つの変更軸がある | ブリッジ | 1つしかない変更にはアダプターまたはストラテジーで対応できる |
| 複雑なサブシステムに、限定された入口が必要である | ファサード/アプリケーションサービス | 無関係な機能まで担う巨大なサービスになる |
| 安定したワークフローに、意図的な拡張ステップがいくつかある | テンプレートメソッドまたはコンポーズしたパイプライン | 継承を使うこと自体が唯一の理由である |
| 横断的なリクエスト処理 | インターセプター、ガード、パイプ、フィルター | コアのビジネスポリシーがドメイン/アプリケーションオブジェクトに属する |
| 完了した事実への独立した反応 | ドメインイベント/統合イベント | 呼び出し側が即時のトランザクション結果を必要とする |
| 明示的なユースケースのメッセージによって価値が生まれる | コマンド/クエリハンドラー | 基本的な CRUD に間接化しかもたらさない |
| DB 書き込みと併せた信頼性の高いイベント発行 | トランザクショナルアウトボックス | ベストエフォートのインプロセス通知で十分である |
パターンを適用する前に、問題は何か、直接的な解決策ではなぜ不十分なのか、そして運用上または認知上どのようなコストが増えるのかを説明してください。
リファクタリングのワークフロー
- 集中的なテストで現在の振る舞いを固定します。
- 現在のコードにある責任と副作用を示します。
- 抽象化を抽出する前に、配置が適切でない振る舞いを本来の担当箇所に移します。
- 観測されたバリエーションまたは境界を中心に、1つの接合点を抽出します。
- 公開契約を不必要に変更せず、Nest のプロバイダーを通じて再接続します。
- 不要になった分岐、ラッパー、抽象化を削除します。
- 変更された境界に応じて、ユニット、モジュール、統合、e2e の各チェックを実行します。
症状からリファクタリング方法を導く際は、references/smells-refactoring.md を参照してください。
テストルール
- プライベートメソッドの呼び出しではなく、公開動作と不変条件をテストします。
- ドメインオブジェクトやポリシーには、通常のユニットテストを使用します。
- コンストラクターの依存関係が単純な場合はユースケースを直接インスタンス化します。テストで Nest の配線やプロバイダーのオーバーライドを検証する必要がある場合は、
TestingModuleを使用します。 - テスト対象のクラスではなく、自分たちが所有する境界をモックまたはフェイクにします。
- 同じポートを複数のアダプターが実装する場合は、コントラクトテストを追加します。
- e2e テストは、トランスポート、バリデーション、認可、シリアライズ、および実際のモジュール配線の検証に使用します。
動作を段階的に表現できる場合、TDD は役に立ちます。ただし、既存の実装を変更する際に、最初に失敗するテストを書いたと誤って主張してはいけません。リポジトリで求められているワークフローに従ってください。
硬直した疑似ルールを拒否する
メソッドは10行以内、クラスは50行以内、オブジェクトのフィールドは2つまで、すべてのプリミティブに値オブジェクトを必須とする、else 文を使わない、といった恣意的な制限を強制してはいけません。凝集度や読みやすさを検討するきっかけとしてのみ使用してください。
次のことを強制してはいけません。
- コンポジションのほうが明快な場合に継承を使うこと。
- すでに適切な永続化境界を包むリポジトリを作ること。
- 単純なコンストラクター1つのためにファクトリーを作ること。
- 同期的な戻り値にドメインイベントを使うこと。
- 関係のない外部コントラクト間で DTO を再利用すること。
- 機能固有の不変条件を失わせる汎用的な
BaseService<T>を作ること。
参照先の案内
| タスク | 読み込む資料 |
|---|---|
| カプセル化、コンポジション、ポリモーフィズム、または SOLID の適用 | oop-solid.md |
| エンティティ、値オブジェクト、ポリシー、サービス、および DTO マッピングの設計 | object-design.md |
| 19 の実践例、Nest の配線、トレードオフ、および振る舞いの確認を使ったパターンの選択 | pattern-catalog.md |
| パターンを Nest のモジュール、プロバイダー、ガード、パイプ、インターセプター、フィルター、および CQRS に対応付ける | nestjs-native-patterns.md |
| コードの臭いを診断し、安全なリファクタリングを選択する | smells-refactoring.md |
期待される回答
設計やレビューでは、次の項目を報告してください。
- 役割と責任: オブジェクトが担うべき責務。
- 根拠: 具体的な結合、不変条件の漏えい、または変更の圧力。
- パターンまたは直接的な設計: 推奨案を1つ挙げ、それが適している理由を示します。
- トレードオフ: 新たに生じる間接性、ライフサイクル、一貫性、またはテストのコスト。
- 変更: 最小限で安全なリファクタリング。
- 検証: 動作とコントラクトの確認。
パターンを使う根拠がない場合は、直接的な実装を推奨してください。