AgentSkillsCN

fsd-architecture

特征切分设计(FSD)架构模式。当您需要处理FSD的各层、切片、依赖规则,或对前端代码进行结构化组织时,可启用此技能。

SKILL.md
--- frontmatter
name: fsd-architecture
description: Feature-Sliced Design architecture patterns. Activated when working with FSD layers, slices, dependency rules, or structuring frontend code.

FSD Architecture

Feature-Sliced Design - frontend standard architecture

Layer Structure

code
src/
├── app/        # App init, providers, router, global styles
├── pages/      # Route entry points (composition only, no logic)
├── widgets/    # Independent UI block compositions
├── features/   # User actions (login, like, comment)
├── entities/   # Business entities (User, Product, Order)
└── shared/     # Common utils, UI Kit, API client

Dependency Rules (Mandatory)

code
app     → pages, widgets, features, entities, shared
pages   → widgets, features, entities, shared
widgets → features, entities, shared
features → entities, shared
entities → shared
shared   → (external libraries only)

Import only from lower layers. No exceptions.

typescript
// features/auth/ui/login-form.tsx
import { User } from '@/entities/user';       // OK: lower layer
import { Button } from '@/shared/ui';          // OK: lower layer

// entities/user/model/hooks.ts
import { useAuth } from '@/features/auth';     // FORBIDDEN: upper layer

// features/auth/ui/login-form.tsx
import { useProfile } from '@/features/profile'; // FORBIDDEN: same layer cross-slice

Slice Structure

code
features/auth/
├── ui/              # Components
│   ├── login-form.tsx
│   └── logout-button.tsx
├── model/           # State, types
│   ├── types.ts
│   └── store.ts
├── api/             # API calls, React Query
│   ├── queries.ts
│   └── mutations.ts
├── lib/             # Utilities
│   └── validate-token.ts
└── index.ts         # Public API (required)

Public API (Required)

Every slice must export through index.ts.

typescript
// features/auth/index.ts
export { LoginForm } from './ui/login-form';
export { LogoutButton } from './ui/logout-button';
export { useLoginMutation } from './api/mutations';
export type { LoginCredentials } from './model/types';
typescript
// OK
import { LoginForm, useLoginMutation } from '@/features/auth';

// FORBIDDEN: direct internal access
import { LoginForm } from '@/features/auth/ui/login-form';

Layer Roles

LayerRoleKey Rules
app/Provider setup, global styles, router configNo business logic
pages/Route entry pointsCompose widgets/features only, no business logic
widgets/Independent UI blocksCombine features + entities, page-ready units
features/User action unitsBusiness logic, mutations live here
entities/Business entitiesType definitions, queries (read-only)
shared/Project-independent codeUI Kit, utilities, API client
typescript
// pages/dashboard/index.tsx
export function DashboardPage() {
  return (
    <PageLayout>
      <DashboardHeader />      {/* widgets */}
      <DashboardStats />       {/* widgets */}
      <RecentActivity />       {/* widgets */}
    </PageLayout>
  );
}
typescript
// widgets/user-profile/index.tsx
export function UserProfile({ userId }: Props) {
  const { data: user } = useUser(userId);
  const { mutate: follow } = useFollowMutation();

  return (
    <Card>
      <UserAvatar user={user} />
      <UserInfo user={user} />
      <FollowButton onFollow={() => follow(userId)} />
    </Card>
  );
}
typescript
// features/like-post/api/mutations.ts
export const useLikePostMutation = () => {
  return useMutation({
    mutationFn: (postId: string) => api.post(`/posts/${postId}/like`),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: postKeys.all });
    },
  });
};
typescript
// entities/user/api/queries.ts
export const userQueries = {
  detail: (id: string) => queryOptions({
    queryKey: ['user', id],
    queryFn: () => api.get<User>(`/users/${id}`),
  }),
};

Naming Conventions

TargetRuleExample
Layerlowercasefeatures, entities
Slicekebab-caseuser-profile, create-post
Component filekebab-caselogin-form.tsx
ComponentPascalCaseLoginForm
HookcamelCaseuseUser, useLoginMutation
TypePascalCaseUser, LoginCredentials

tsconfig Paths

json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Circular Reference Resolution

When circular references occur, move shared types to shared:

typescript
// shared/types/index.ts
export interface BaseUser { id: string; name: string; }
export interface BasePost { id: string; authorId: string; }

DO NOT

  • Import from upper layers
  • Import from same-layer sibling slices
  • Bypass index.ts public API
  • Put business logic in pages/ layer
  • Put mutations in entities/ layer (read-only)