architecture-and-design について
TypeScript の Web アプリケーション設計とレビューに使える、フレームワーク非依存のスキルです。コード構造、状態、型安全性、セキュリティ、テストなどの実行可能なルールを備え、生成・レビュー・リファクタリングに対応します。
主な機能
- 01生成・レビュー・リファクタリングの各モード
- 02コンポーネントとコード構造のルール
- 03型安全性と状態管理の指針
- 04セキュリティとフロントエンドの確認項目
- 05アーキテクチャとテストの原則
活用シーン
- 01TypeScript の Web アプリを設計する
- 02アーキテクチャ規則に沿ってプルリクエストを確認する
- 03動作を保ちながらコードをリファクタリングする
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ danielteles/ awesome-agent-skills --skill architecture-and-designインストール後、Claude Code で/architecture-and-designを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/adr-template.mdreferences/clean-architecture.mdclean-code.mdddd.mdexpressive-logic.mdforms.mdfrontend-practices.mdmicro-frontends.mdpatterns.mdsecurity.mdsolid.mdstate-and-data.mdstructure.mdtesting.mdtype-safety.mdworked-example.md詳細
アーキテクチャと設計 — Base Engineering Skill
TypeScript の Web アプリケーション向けの、フレームワークに依存しないアーキテクチャ標準、設計原則、構造パターンです。これらの原則は React、Angular、Vue、およびあらゆるコンポーネントベースのフレームワークに当てはまります。コード例では具体的な構文として React/TSX を使用していますが、各原則は Angular のテンプレートや Vue SFC にも相当する形があります。
基盤とするスキル。
core-typescript(言語構文)を基盤とします。共通するトピックでは、このスキルが設計を、core-typescriptが構文を担当します。react、angular、またはvueはフレームワーク固有の形式を、accessibilityは UI の観点を提供します。タスクがそのレイヤーに関わる場合にのみ関連スキルを読み込んでください。読み込まれていない場合は、一般知識に基づいてそのレイヤーを適用し、作業を止めないでください。
この SKILL.md は単独で利用できます。以下の ルールセット が、完全で適用可能なルール一覧です。各 references/<topic>.md には該当グループの考え方と ❌ / ✅ のコード例が記載されており、references/worked-example.md にはレビュー全体の例があります。実行環境が許す場合は、詳細を確認するために開いてください。
このスキルの使い方
タスクに合ったモードを選び、手順を順番に進めてください。
| モード | 手順 |
|---|---|
| 生成 — 新しいコンポーネント、フック、またはモジュールを作成する | 1. 作成しながら solid、clean-code、expressive-logic、type-safety、state-and-data を適用します。 2. 非同期状態は判別可能なユニオンとしてモデル化します(patterns)。 3. 外部データは境界で検証します(type-safety)。 4. チェックリストとしてルールセットを実行し、引き渡す前に各不備を修正します。 |
| レビュー — プルリクエストまたは差分を確認する | 1. 差分に対してルールセットを実行します。 2. 不備ごとに、以下の出力形式で指摘を1件記述します。 3. 指摘は must-fix を先に、その後に consider の順で並べます。 4. 不備がなければ、その旨を1行で伝えます。指摘をでっち上げないでください。 |
| リファクタリング — 既存のコードを再構成する | 1. 振る舞いを維持したまま構造を変更します。 2. clean-code、expressive-logic、state-and-data を活用します。 3. 1つのコミットにつき変更の種類は1つにします。 4. テストをすべて成功させます(testing)。 |
出力形式
1行につき指摘を1件記述します。
<severity> · <topic> · <file>:<line> — <what is wrong>. <the fix as an action>.
<severity>はmust-fix(このスキルのルールに違反)またはconsider(安全ですが、ルールでは別の形式を推奨)です。<topic>はルールセットのトピックのスラッグです(state-and-data、type-safety、securityなど)。
すべてのモードに適用するルール
- ルールを適用する際は、ルールセットのトピック名を明記します。
- ルールだけでなく、その理由も述べます。「状態を下位に移動する」よりも、「これによりツリー全体が再レンダリングされます」のほうが適切です。
- ルール名と変更内容を明記します。作者のコードを黙って書き換えないでください。
- 原則はコードのためにあります。2つの原則が衝突する場合は、読みやすく変更しやすいほうを選び、その理由を述べます。
- タスクが差分ではなく意思決定の場合は、
solid、clean-architecture、structure、state-and-data、micro-frontendsに照らして選択肢を評価し、リポジトリ内にassets/adr-template.mdを使って ADR(コンテキスト、選択肢、決定、結果)を記録します。
ルールセット
solid → references/solid.md
- SRP: レンダリングをデータ取得、状態遷移、ドメイン計算から分離します(コンポーネント、フック、サービス、純粋関数に分けます)。
- OCP: コンポーネントの可変部分は
children、スロット、またはレンダープロップで表現し、バリアントごとにブール値フラグを設けないでください。 - LSP: カスタムプリミティブやモックは、利用側が期待するインターフェース全体を満たします。
- ISP: コンポーネントには、ドメインエンティティ全体ではなく、読み取るプロパティだけを渡します。
- DIP: ビジネスロジックと UI は、具体的なクライアント、SDK、または
localStorageではなく、注入されたインターフェース、トークン、またはコンテキストに依存します。
clean-code → references/clean-code.md
- ブール値には
is/has/should/canを付け、関数名は動詞にします。同じ概念には同じ用語を使い、不明瞭な略語は避けます。 - 関数はスクロールせずに読める長さにします。パラメーターリストの形式については、
core-typescriptの functions を参照してください。 - 動作を切り替えるブール値のフラグ引数は使わず、代わりに関数を2つ用意します。
- コメントには 何をするか ではなく なぜそうするか を記述します。デッドコード(コメントアウトされたブロック、未使用のエクスポート、到達不能な分岐)は残しません。
- パラメーター、プロパティ、状態をその場で変更せず、新しい値を作成して返します。
- すべてのエラーを処理するか、再スローします。空の
catchは使いません。
expressive-logic → references/expressive-logic.md
-
x ? true : false、if (c) return true; else return false、ブールリテラルとの比較は使いません。 - ネストした条件分岐よりガード節を使い、すべての条件は肯定形で記述します。
- 長いブール式の連鎖には名前(変数または述語関数)を付けます。
- 手動の
&&による null チェーンより?./??を使います(??が||より適切な場合については、core-typescriptの nullability を参照してください)。 - マジックナンバーとマジック文字列には名前付き定数を使います。
- 1つの値に対する
if/else ifの連鎖は、型付きルックアップ、またはassertNeverのデフォルト分岐を持つ網羅的なswitchにします。
type-safety → references/type-safety.md
- 外部入力は
unknownとして受け取り、境界で型を絞り込みます。anyとキャストの構文ルールについては、core-typescriptの unsafe-types を参照してください。 - 相互排他的な状態は、オプショナルフィールドを持つオブジェクトではなく、判別共用体で表します。
-
readonly、as const、ブランド付き ID 型を使い、不正な値を表現できないようにします。 - ネットワークデータはアダプターでスキーマに照らして解析してから、ドメインモデルにマッピングします。
- API 型は手書きせず、コントラクト(OpenAPI、GraphQL codegen、tRPC)から生成します。
- 環境設定とランタイム設定は、起動時にスキーマに照らして検証します。
clean-architecture → references/clean-architecture.md
- ドメインコアには、フレームワーク、DOM、ネットワークのインポートを一切含めません。
- 依存関係は内側を向くようにします。UI → ユースケース → ドメインの順に依存し、インフラストラクチャはドメインのインターフェースを実装します(依存関係逆転)。
structure → references/structure.md
- コードは技術的な役割ではなく、ビジネス機能ごとに整理します(
components//services//utils/のような雑多な置き場は作りません)。 -
featuresはsharedをインポートできますが、sharedからfeaturesをインポートしてはいけません。 - ある機能から別の機能の内部ファイルをインポートしてはいけません。インポートできるのは、その機能の
index.ts、共有状態、またはルーティングのみです。 - 境界はリンター(
eslint-plugin-boundaries、Nx のモジュール境界)で強制します。
patterns → references/patterns.md
- サードパーティライブラリ、SDK、生の API レスポンスは、ドメインモデルを返すアダプターまたはリポジトリの背後に隠します。
- 非同期状態は
statusを判別子とする判別共用体にします。isLoading/isError/hasDataのような個別のブール値は使いません。
state-and-data → references/state-and-data.md
- 状態はそれを使うコンポーネント内に配置し、別のコンポーネントでも必要になった場合にのみ親へ引き上げます。
- props や他の状態から計算できる値は、保存せず導出します。
- クライアントストアに保持するサーバーコレクションは正規化します。各エンティティは一度だけ保持し、ID をキーにします。クエリーキャッシュ内のデータは取得時のままにします。
- 状態の種類ごとに適切な層に配置します。サーバーキャッシュライブラリ/URL パラメーター/小規模なストアまたはコンテキスト/コンポーネントを使います。
- コンテキストには更新頻度の低い値だけを保持します。頻繁に変わる値には、広範囲に影響するコンテキストではなく、セレクターを備えたストアを使います。
- サーバーデータは入力値をキーとするキャッシュライブラリ経由で取得します。エフェクト内で取得してローカル状態に保存する方法は使いません。
- サーバーデータは、遠く離れた祖先コンポーネントから props として渡さず、使用箇所で取得します。
- 独立したリクエストは並列で読み込み、親から子へ順に読み込むウォーターフォールを避けます。
- ミューテーションのレスポンスを使ってキャッシュを更新します。楽観的更新にはロールバック手段を用意します。
- クエリーの種類ごとに、明示的な stale time と再試行ポリシーを設定します。
frontend-practices → references/frontend-practices.md
- フィルター、マップ、チェーンは、テンプレートの外で return の前に計算します。
- すべての非同期状態をモデル化します。読み込み中、空、エラー、成功。
- リストのキーには安定した ID を使用します。並べ替え可能なリストに配列のインデックスは使用しません。
- ライフサイクルの副作用より、派生値またはイベントハンドラーを優先します。副作用を残す場合は、単一の関心事を扱い、クリーンアップを行います。
- 独立した各領域に、それぞれエラーバウンダリーとフォールバックを設けます。アプリのルートだけに設けることは避けます。
- 古くなった非同期結果はキャンセルするか、無視します(
AbortControllerまたは無視フラグを使用)。 - 捕捉したエラーは、ソースマップ、リリースタグ、セッションコンテキストを付けてトラッカーに送信します。
-
console.logをリリースに含めません。ログ出力は単一の構造化ロガーを通します。 - ハンドラーを付けた
divより、セマンティック要素を優先します。すべての入力とアイコンのみのコントロールにラベルを付けます。アクセシビリティの全項目については、accessibilityを参照してください。
security → references/security.md
- 信頼できない入力から HTML を構築しません。HTML として描画する必要があるものは、まずサニタイズします(DOMPurify)。
-
dangerouslySetInnerHTML/[innerHTML]/v-htmlと、すべてのbypassSecurityTrust*を避けます。残す場合は、それぞれレビューします。 - バンドルに秘密情報を含めません。サードパーティのキーはサーバーに保管します。
-
target="_blank"を指定したすべてのリンクにrel="noopener noreferrer"を設定します。 - クエリパラメーターのリダイレクト URL は許可リストに照らして検証します。ユーザーデータ由来の
javascript:またはdata:URL は拒否します。 - トークンの保存方法は意図を持って選び、トレードオフを記録します。
- CSP は強制モードにし、スクリプトには nonce ベースの設定を使います。ブラウザーが対応している場合は Trusted Types を使用します。
- Cookie 認証では、状態を変更するすべてのリクエストに CSRF 対策トークンを付け、
SameSiteを設定します。 - サードパーティの
<iframe>には、必要な機能だけを許可するsandboxを設定します。 - 新しい依存関係は、追加前にメンテナンス状況、推移的なサイズ、出所を確認します。
testing → references/testing.md
- テストスイートは統合テストを中心に構成します。静的解析を土台とし、純粋なロジックにはユニットテストを用い、ネットワークを境界でモックしたコンポーネントと協調要素のテストを厚くし、重要な経路に対するエンドツーエンドテストは薄く保ちます。
- ドメインとユースケースには、DOM やネットワークに依存しない、プレーンな関数の高速なユニットテストを用います。
- コンポーネントはロールとラベルで取得し、ユーザーに見える内容を検証します。適切なアクセシブルな名前がない場合に限り、
data-testidを使用します。 - 全体のカバレッジ率ではなく、変更が触れた行に対してカバレッジを強制します。
- すべてのバグ修正に、修正前は失敗するテストを追加します。
- API 境界はコントラクトテストで検証するか、コントラクトから型を生成してペイロードを検証します。
- 不安定なテストは、最初の不安定な結果が出た時点で隔離し、スプリント内に修正します。
- スナップショットは、小さく安定した出力に限って使用します。
- 各テストは
test-qualityRuleset にも合格する必要があります。実装ではなく振る舞いを検証し、意味のあるアサーションを含み、決定的で、実行順序に依存しないこと。このグループはテストスイートの 構成 を決め、test-qualityは個々のテストを評価します。
ddd → references/ddd.md
- コード、テスト、UI、API 全体で、概念ごとに共通の名前を一つ使います(どこでも
Cartとし、Basket/Orderは使いません)。 - エンティティと値オブジェクトは、フレームワークやネットワークのコードから分離して、機能の
model/に配置します。 - 値オブジェクトは不変にします。変更する場合は新しいインスタンスを作成します。
- 集約の内部要素には、ルートを通じてのみアクセスします。
- ドメインイベントには過去形の名前を付け、命令ではなく事実として扱います。
- 境界づけられたコンテキストごとにモデルを一つ用意し、境界で変換します。
マイクロフロントエンド → references/micro-frontends.md
- デフォルトはモジュラーモノリスです。チームごとの独立したデプロイが実際のボトルネックになった場合にのみ分割します。
- シェルと各リモートは、マウント関数、受け取る props、送出するイベントで構成されるバージョン管理されたコントラクトを共有します。
- フレームワークとデザインシステムは、固定バージョンのシングルトンとして共有します。
- リモートの読み込みに失敗したり、例外が発生したりしても、シェルが空白にならないようにします。
- ルーティングと認証はシェルに保持し、ID は下位に渡します。
フォーム → references/forms.md
- 下書きの状態はフォームまたはフォームライブラリ内に保持し、グローバル状態には決して引き上げません。
- ドメインモデルの隣に検証スキーマを1つ定義し、クライアントとサーバーで再利用します。
-
isValid/isDirty/ フィールドごとのエラーはフォームの状態から導出し、保存しません。 - 大規模なフォームにはフォームライブラリを使った非制御入力を使用します。フィールドコンポーネントの制御/非制御のコントラクトについては
component-api-designを参照してください。 - サーバー側の検証エラーは、該当するフィールドに紐付けます。
- 送信処理中は送信を無効にし、送信に失敗しても入力値を保持します。
適用範囲
このスキルではフロントエンドのアーキテクチャと設計を扱います。以下は対象外です。
- バックエンド、データベース、インフラストラクチャの設計。
- 分散システム(マイクロサービス、イベント駆動型バックエンド、サガ)。
dddでは1つのアプリ内の戦術的パターンを扱い、micro-frontendsではフロントエンドの分割のみを扱います。 - スタイリング、トークン、CSS アーキテクチャ —
styling-and-design-tokens。 - 「セマンティックな要素とラベル」以上のアクセシビリティ —
accessibility。 - パフォーマンス予算、Core Web Vitals、バンドル分析 —
web-performance。 - 再利用可能なコンポーネントの公開 API(
component-api-design)と国際化(i18n-and-localization)。 - CI 設定、依存関係管理ボット、リリースツール、モノレポのセットアップ。
- フレームワーク固有のルール(フック、メモ化、変更検知、リアクティビティ)—
react/angular/vue。
このスキルでは原則を示します。コードを読み、ドメインを理解することの代わりにはなりません。
参考資料
このスキルは、以下と組み合わせて使用します。
core-typescript— 言語の基盤です。共通のトピックでは、このスキルが設計を定め、core-typescriptが構文を定めます。react/angular/vue— これらのルールを各フレームワークに適用したものです。React、Angular、Vue のコードベースでは該当するものを適用し、直接的な矛盾がある場合はそちらを優先します。accessibility— UI のレビュー観点(ARIA、キーボードとフォーカス、ライブリージョン、アクセシビリティテスト)。test-quality— このスキルのtestingグループではテストスイートの構成を定め、test-qualityでは個々のテストを評価します。