application-patterns について
application-patternsは、実用的なアプリケーション開発で繰り返し使われるパターンをまとめたリファレンススキルです。アプリケーション開発者向けに、CRUD、認証、管理画面、ファイル処理、検索、ワークフロー、国際化を扱います。
主な機能
- 01CRUDのデータフローとフォーム検証
- 02認証とセッション方式
- 03管理テーブルと一括操作
- 04ファイルアップロードと画像処理
- 05検索アーキテクチャと機能チェックリスト
- 06ワークフローパターンと国際化
活用シーン
- 01CRUDフォームからデータベースまでの流れの設計
- 02認証とセッション方式の選択
- 03ファイルアップロードや検索構成の設計
- 04承認ワークフローと翻訳の構成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ miles990/ claude-software-skills --skill application-patternsインストール後、Claude Code で/application-patternsを入力して読み込まれたか確認します。
詳細
アプリケーション開発パターン
概要
実用的なアプリケーションを構築するための一般的なパターンです。これらのパターンは、アプリケーション開発で繰り返し発生する問題を解決します。
CRUD アプリケーション
データフローパターン
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Form │ ──→ │Validate │ ──→ │ Service │ ──→ │ DB │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
↑ │
└───────────── Response ←───────────────────────┘
フォーム処理のベストプラクティス
// 1. Validation schema (shared frontend/backend)
const userSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
role: z.enum(['admin', 'user', 'guest'])
});
// 2. Server action with error handling
async function createUser(formData: FormData) {
const result = userSchema.safeParse(Object.fromEntries(formData));
if (!result.success) {
return { error: result.error.flatten() };
}
try {
const user = await db.user.create({ data: result.data });
return { success: true, data: user };
} catch (e) {
if (e.code === 'P2002') {
return { error: { email: 'Email already exists' } };
}
throw e;
}
}
ユーザー認証
認証フロー
┌────────────────────────────────────────────────────────────┐
│ Authentication Flows │
├────────────────────────────────────────────────────────────┤
│ │
│ Email/Password: │
│ Login → Validate → Create Session → Set Cookie → Redirect │
│ │
│ OAuth (Social Login): │
│ Redirect → Provider Auth → Callback → Upsert User → Done │
│ │
│ Magic Link: │
│ Email → Generate Token → Send Link → Verify → Login │
│ │
└────────────────────────────────────────────────────────────┘
セッション管理
| 戦略 | メリット | デメリット |
|---|---|---|
| JWT | ステートレスでスケーラブル | 簡単には無効化できない |
| サーバーセッション | 無効化可能で安全 | セッションストアが必要 |
| ハイブリッド | 両方の長所を備える | より複雑 |
セキュリティチェックリスト
- パスワードのハッシュ化(bcrypt/argon2)
- ログイン時のレート制限
- CSRF 保護
- セキュアな Cookie 設定(httpOnly、secure、sameSite)
- ログイン失敗後のアカウントロック
- パスワードリセットトークンの有効期限
管理ダッシュボード
データテーブルパターン
// Reusable data table with sorting, filtering, pagination
interface DataTableProps<T> {
data: T[];
columns: ColumnDef<T>[];
pagination: { page: number; pageSize: number; total: number };
sorting: { field: string; direction: 'asc' | 'desc' }[];
filters: Record<string, unknown>;
onStateChange: (state: TableState) => void;
}
// Server-side handling
async function getUsers(params: TableState) {
const { page, pageSize, sorting, filters } = params;
const query = {
where: buildWhereClause(filters),
orderBy: buildOrderBy(sorting),
skip: (page - 1) * pageSize,
take: pageSize,
};
const [users, total] = await Promise.all([
db.user.findMany(query),
db.user.count({ where: query.where })
]);
return { data: users, total };
}
一括操作
// Safe bulk delete with confirmation
async function bulkDelete(ids: string[]) {
// 1. Validate permissions for each item
const items = await db.item.findMany({
where: { id: { in: ids } },
select: { id: true, ownerId: true }
});
const authorized = items.filter(item =>
canDelete(currentUser, item)
);
// 2. Soft delete or hard delete
await db.item.updateMany({
where: { id: { in: authorized.map(i => i.id) } },
data: { deletedAt: new Date() }
});
return {
deleted: authorized.length,
skipped: ids.length - authorized.length
};
}
ファイル管理
アップロード戦略
| 方法 | 用途 | 最大サイズ |
|---|---|---|
| サーバーへ直接 | 小さなファイル | 約10MB |
| 署名付き URL | 大きなファイル | 無制限 |
| チャンク分割アップロード | 非常に大きなファイル | 無制限 |
| 再開可能なアップロード | 不安定なネットワーク | 無制限 |
署名付き URL のフロー
Client Server S3
│ │ │
│── Request upload URL ──→│ │
│ │── Generate presigned ─→│
│←── Return presigned URL─│ │
│ │ │
│───────── Upload file directly ─────────────────→│
│ │ │
│── Confirm upload ──────→│ │
│ │── Verify file exists ─→│
│←── Success ─────────────│ │
画像処理パイプライン
async function processUpload(file: File) {
// 1. Validate file type and size
if (!ALLOWED_TYPES.includes(file.type)) {
throw new Error('Invalid file type');
}
// 2. Generate variants
const variants = await Promise.all([
sharp(file.buffer).resize(100, 100).toBuffer(), // thumbnail
sharp(file.buffer).resize(800, 600).toBuffer(), // medium
sharp(file.buffer).resize(1920, 1080).toBuffer(), // large
]);
// 3. Upload to CDN
const urls = await uploadToS3(variants);
// 4. Store metadata
return db.image.create({
data: {
original: urls.original,
thumbnail: urls.thumbnail,
medium: urls.medium,
large: urls.large,
mimeType: file.type,
size: file.size,
}
});
}
検索の実装
検索アーキテクチャ
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Database │ ──→ │ Sync │ ──→ │ Search │
│ (Primary) │ │ Worker │ │ Engine │
└──────────────┘ └──────────────┘ └──────────────┘
↑
│
┌──────────────┐ ┌──────────────┐ │
│ Client │ ──→ │ Search API │ ────────────┘
└──────────────┘ └──────────────┘
検索機能のチェックリスト
- 全文検索
- ファセットフィルタリング
- オートコンプリート/候補表示
- 誤字の許容(あいまい一致)
- ハイライト表示
- 同義語
- 関連度の調整
ワークフローエンジン
ステートマシンパターン
const orderStateMachine = {
initial: 'pending',
states: {
pending: {
on: {
PAY: 'paid',
CANCEL: 'cancelled'
}
},
paid: {
on: {
SHIP: 'shipped',
REFUND: 'refunded'
}
},
shipped: {
on: {
DELIVER: 'delivered',
RETURN: 'returned'
}
},
delivered: { type: 'final' },
cancelled: { type: 'final' },
refunded: { type: 'final' },
returned: {
on: {
REFUND: 'refunded'
}
}
}
};
承認ワークフロー
interface ApprovalStep {
id: string;
approvers: string[]; // User IDs or roles
requiredApprovals: number; // How many need to approve
timeout?: Duration; // Auto-escalate after
escalateTo?: string; // Next approver on timeout
}
async function processApproval(stepId: string, userId: string, decision: 'approve' | 'reject') {
const step = await db.approvalStep.findUnique({ where: { id: stepId } });
// Record decision
await db.approval.create({
data: { stepId, userId, decision, timestamp: new Date() }
});
// Check if complete
const approvals = await db.approval.count({
where: { stepId, decision: 'approve' }
});
if (approvals >= step.requiredApprovals) {
await advanceToNextStep(step);
}
}
多言語対応(i18n)
翻訳の構成
locales/
├── en/
│ ├── common.json # Shared strings
│ ├── auth.json # Auth module
│ └── dashboard.json # Dashboard module
├── zh-TW/
│ ├── common.json
│ ├── auth.json
│ └── dashboard.json
└── ja/
└── ...
ベストプラクティス
-
キーの命名:名前空間付きのキーを使用する
{ "auth.login.title": "Sign In", "auth.login.email": "Email Address", "auth.login.submit": "Sign In" } -
複数形:複数形を適切に処理する
{ "items": "{count, plural, =0 {No items} =1 {1 item} other {# items}}" } -
変数:補間を使用する
{ "welcome": "Welcome, {name}!" } -
日付/数値の書式設定:Intl API を使用する
new Intl.DateTimeFormat(locale).format(date) new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount)
関連スキル
- [[architecture-patterns]] - システム全体の設計
- [[frontend]] - UI の実装
- [[backend]] - サーバーの実装
- [[database]] - データの永続化
- [[security-practices]] - セキュリティ上の考慮事項
コメント
Markdown 対応