Files
vibe_coding/.cursor/rules/references/019-modular-deep.md
2026-03-05 21:27:11 +08:00

15 KiB
Raw Blame History

019-modular.mdc (Deep Reference)

该文件为原始详细规范归档,供 Tier 3 按需读取。


🧩 Modular Architecture Standards

核心原则

原则 说明 违反案例
单一职责 一个文件只做一件事 1000 行的 index.vue 包含业务+状态+样式
高内聚低耦合 同模块内紧密,跨模块通过接口 直接引用另一模块的内部 Service
依赖方向单一 只允许 UI → Service → Repository → Model Service 引用 Controller
显式边界 模块通过公开的 index.ts 暴露 API import { InternalHelper } from '../order/utils/internal'
禁止循环依赖 A 不能引用 B 的同时 B 引用 A order 模块和 user 模块互相引用

前端模块划分

标准目录结构

src/
├── api/                     # API 请求层(按模块分文件)
│   ├── user.ts              # 用户相关接口
│   ├── order.ts             # 订单相关接口
│   ├── permission.ts        # 权限相关接口
│   └── index.ts             # 统一导出
│
├── composables/             # 可复用逻辑(按功能命名 use*.ts
│   ├── useTable.ts          # 表格通用逻辑
│   ├── useForm.ts           # 表单通用逻辑
│   ├── useAuth.ts           # 认证状态
│   ├── useDevice.ts         # 设备检测
│   ├── usePermission.ts     # 权限检查
│   └── useWebSocket.ts      # WebSocket 管理
│
├── stores/                  # Pinia 状态(按模块分文件)
│   ├── user.ts
│   ├── menu.ts
│   ├── setting.ts
│   └── index.ts             # 统一导出
│
├── components/              # 全局公共组件
│   ├── ArtTable/            # 每个复杂组件独立目录
│   │   ├── index.vue        # 主组件
│   │   ├── TableToolbar.vue # 子组件(内聚)
│   │   ├── TablePagination.vue
│   │   └── types.ts         # 组件专属类型
│   ├── ArtForm/
│   └── ArtChart/
│
├── views/                   # 页面(按业务域分目录)
│   ├── order/               # 订单业务域
│   │   ├── index.vue        # 列表页
│   │   ├── detail.vue       # 详情页
│   │   ├── components/      # 页面专属组件(不对外)
│   │   │   ├── OrderCard.vue
│   │   │   └── OrderTimeline.vue
│   │   ├── composables/     # 页面专属 composable
│   │   │   └── useOrderFilter.ts
│   │   └── types.ts         # 页面专属类型
│   │
│   ├── user/
│   ├── permission/
│   └── dashboard/
│
├── utils/                   # 纯工具函数(无副作用)
│   ├── format.ts            # 格式化
│   ├── validate.ts          # 校验
│   ├── crypto.ts            # 加密工具
│   └── date.ts              # 日期工具
│
├── directives/              # 自定义指令(每个指令独立文件)
│   ├── auth.ts              # v-auth
│   ├── roles.ts             # v-roles
│   └── index.ts             # 统一注册
│
└── types/                   # 全局类型定义
    ├── api.ts               # API 响应/请求类型
    ├── models.ts            # 业务实体类型
    └── common.ts            # 通用类型

文件拆分阈值

指标 警告 必须拆分
文件行数 > 200 行 > 400 行
组件内 ref 数量 > 8 个 > 15 个
单个 composable 行数 > 100 行 > 200 行
单个 store 行数 > 150 行 > 300 行

组件拆分决策树

一个 .vue 文件是否应该拆分?
  ├─ 文件 > 200 行? → 考虑拆分
  ├─ 包含多个独立 UI 块? → 拆分为子组件
  ├─ 业务逻辑超过 50 行? → 提取为 composable
  ├─ 该组件在 > 2 个地方复用? → 移至 components/
  └─ 否 → 保持不变

后端模块划分Hyperf DDD

标准目录结构

app/
├── Controller/          # HTTP 入口层(只做参数接收 + 响应格式化)
│   ├── OrderController.php
│   ├── UserController.php
│   └── Traits/
│       └── ApiResponse.php   # 响应格式 Trait
│
├── Service/             # 业务逻辑层(核心业务规则)
│   ├── OrderService.php
│   ├── UserService.php
│   └── Dto/             # 数据传输对象
│       ├── OrderCreateDto.php
│       └── OrderListDto.php
│
├── Repository/          # 数据访问层(数据库/缓存操作)
│   ├── OrderRepository.php
│   ├── UserRepository.php
│   └── Contracts/       # Repository 接口
│       └── OrderRepositoryInterface.php
│
├── Model/               # Eloquent ORM 模型
│   ├── Order.php
│   ├── User.php
│   └── Traits/          # 可复用模型 Trait
│       ├── HasCreator.php
│       ├── HasSoftDeletes.php
│       └── HasDataPermission.php
│
├── Request/             # 表单验证(每个操作独立 Request
│   ├── Order/
│   │   ├── CreateOrderRequest.php
│   │   ├── UpdateOrderRequest.php
│   │   └── ListOrderRequest.php
│   └── User/
│
├── Event/               # 事件定义(命名:名词+动词过去式)
│   ├── OrderCreated.php
│   ├── OrderStatusChanged.php
│   └── UserLoggedIn.php
│
├── Listener/            # 事件监听器(一个事件一个 Listener
│   ├── SendOrderNotification.php
│   ├── LogOrderStatusChange.php
│   └── UpdateUserLastLogin.php
│
├── Middleware/          # 中间件(每个职责独立文件)
│   ├── AuthMiddleware.php
│   ├── PermissionMiddleware.php
│   ├── RateLimitMiddleware.php
│   └── AntiScrapingMiddleware.php
│
├── Exception/           # 异常定义(每类业务一个异常)
│   ├── BusinessException.php
│   ├── AuthException.php
│   ├── PermissionException.php
│   └── Handler/
│       └── AppExceptionHandler.php
│
├── Job/                 # 异步任务AsyncQueue
│   ├── SendNotificationJob.php
│   └── GenerateReportJob.php
│
└── Command/             # 命令行工具
    └── SyncPermissionCommand.php

各层职责边界

// ✅ Controller — 只做参数绑定 + 调用 Service + 格式化响应
class OrderController
{
    public function store(CreateOrderRequest $request): array
    {
        $dto = CreateOrderDto::fromRequest($request);
        $order = $this->orderService->create($dto);
        return $this->success(OrderResource::make($order));
    }
}

// ✅ Service — 只做业务逻辑,调用 Repository 取数据
class OrderService
{
    public function create(CreateOrderDto $dto): Order
    {
        // 业务规则:库存检查
        $this->inventoryService->checkStock($dto->productId, $dto->quantity);

        $order = $this->orderRepository->create($dto->toArray());

        // 发事件(不直接发通知,解耦)
        event(new OrderCreated($order));

        return $order;
    }
}

// ✅ Repository — 只做数据库操作,不包含业务规则
class OrderRepository
{
    public function create(array $data): Order
    {
        return Order::create($data);
    }

    public function findWithItems(int $id): ?Order
    {
        return Order::with(['items', 'creator'])->find($id);
    }
}

// ❌ 反模式Controller 直接操作 Model
class BadController
{
    public function store(Request $request): array
    {
        $order = Order::create($request->all()); // ← 跳过 Service 层
        Mail::to($order->user)->send(new OrderMail($order)); // ← Controller 不应发邮件
        return ['data' => $order];
    }
}

模块通信规范

通信方式 使用场景 实现
直接依赖注入 同层级调用Service → Service __construct 注入
事件系统 跨模块解耦通知 event(new OrderCreated($order))
消息队列 耗时操作异步化 AsyncQueue::push(new SendEmailJob(...))
共享 Repository 跨模块读取数据 注入对方 Repository只读
禁止 跨模块调用内部方法 $userService->_internalCalc()
// ✅ 事件解耦:订单模块不直接依赖通知模块
// app/Event/OrderCreated.php
class OrderCreated
{
    public function __construct(public readonly Order $order) {}
}

// app/Listener/SendOrderNotification.php通知模块监听
#[Listener]
class SendOrderNotification implements ListenerInterface
{
    public function listen(): array
    {
        return [OrderCreated::class];
    }

    public function process(object $event): void
    {
        $this->notificationService->notify($event->order->user_id, 'order_created', [
            'order_id' => $event->order->id,
        ]);
    }
}

禁止循环依赖

依赖方向规则(严格单向):

Controller → Service → Repository → Model
                ↓
            Event → Listener
                ↓
            Job异步

✅ Service 可以依赖 Repository
✅ Service 可以发布 Event
✅ Listener 可以依赖 Service
❌ Repository 不能依赖 Service
❌ Model 不能依赖 Service/Repository
❌ Event 不能依赖 ServiceEvent 是纯数据)

检测循环依赖

# PHP 项目
composer require maglnet/composer-require-checker --dev

# 前端项目ESLint 插件)
npm install eslint-plugin-import --save-dev
# .eslintrc 中配置 import/no-cycle

Vue 组件模块化规范

组件分类

类型 位置 命名 特征
基础组件 src/components/ Art* 无业务逻辑,高复用
业务组件 src/views/{domain}/components/ {Domain}* 包含业务,不跨域复用
页面组件 src/views/{domain}/index.vue 路由直接映射 整合子组件和 store
布局组件 src/layouts/ *Layout 全局页面框架

单文件组件结构SFC

<!-- 标准顺序script  template  style -->
<script setup>
// 1. 导入(分组排列)
import { computed, ref } from 'vue'        // Vue core
import { useRouter } from 'vue-router'      // Router
import { storeToRefs } from 'pinia'        // Pinia
import { useOrderStore } from '@/stores/order'  // Stores
import { useTable } from '@/composables/useTable'  // Composables
import { OrderApi } from '@/api/order'      // API
import OrderCard from './components/OrderCard.vue'  // Local components

// 2. Props & EmitsJS 对象语法)
const props = defineProps({
  domain: { type: String, required: true },
  readonly: { type: Boolean, default: false },
})

const emit = defineEmits(['updated', 'deleted'])

// 3. Store
const orderStore = useOrderStore()
const { orders, loading } = storeToRefs(orderStore)

// 4. Composable复杂逻辑提取
const { tableData, pagination, fetchData } = useTable(OrderApi.list)

// 5. 本地状态(按功能分组,加注释分隔)
// --- Dialog state ---
const dialogVisible = ref(false)
const currentOrder = ref(null)

// --- Filter state ---
const searchForm = ref({ keyword: '', status: '' })

// 6. Computed
const filteredOrders = computed(() =>
  orders.value.filter((o) => o.domain === props.domain)
)

// 7. Methods按功能分组
function openDialog(order) {
  currentOrder.value = order
  dialogVisible.value = true
}

async function handleDelete(id) {
  await orderStore.delete(id)
  emit('deleted', id)
}

// 8. Lifecycle
onMounted(() => fetchData())
</script>

<template>
  <!-- 模板只做渲染不包含复杂逻辑 -->
</template>

<style scoped>
/* 组件作用域样式 */
</style>

Composable 拆分规范

// ✅ 单一职责useOrderFilter 只负责过滤逻辑
// src/views/order/composables/useOrderFilter.ts
export function useOrderFilter(orders) {
  const status = ref('')
  const keyword = ref('')
  const dateRange = ref(null)

  const filtered = computed(() => {
    return orders.value.filter((order) => {
      if (status.value && order.status !== status.value) return false
      if (keyword.value && !order.orderNo.includes(keyword.value)) return false
      if (dateRange.value) {
        const [start, end] = dateRange.value
        if (order.createdAt < start || order.createdAt > end) return false
      }
      return true
    })
  })

  function reset() {
    status.value = ''
    keyword.value = ''
    dateRange.value = null
  }

  return { status, keyword, dateRange, filtered, reset }
}

// ❌ 反模式:把表格、过滤、编辑逻辑都放在一个 composable
export function useOrderPage() { // 太大了
  // ... 300 行混合逻辑
}

前端导入层级边界

导入方向规则(严格单向):

views/ → composables/ → stores/ → api/ → utils/
  │          │
  │          └→ components/(仅消费,不反向引用 views/
  └→ components/

✅ views/ 可以引用 composables/、stores/、api/、components/
✅ composables/ 可以引用 stores/、api/、utils/
✅ stores/ 可以引用 api/、utils/
✅ components/ 可以引用 composables/、utils/(通用组件不引用 stores/
❌ components/core/ 不得引用 stores/(通用组件不依赖业务状态)
❌ api/ 不得引用 stores/ 或 views/
❌ utils/ 不得引用任何其他层(纯函数,无副作用)
❌ 跨业务域直接引用内部组件(通过公开 index.ts 导出)

ESLint 配置建议

// .eslintrc — import/no-restricted-paths
{
  rules: {
    'import/no-restricted-paths': ['error', {
      zones: [
        { target: './src/utils', from: './src/stores' },
        { target: './src/utils', from: './src/api' },
        { target: './src/api', from: './src/stores' },
        { target: './src/components/core', from: './src/stores' },
      ],
    }],
  },
}

规则汇总

  • 单个 .vue 文件超过 200 行必须拆分,超过 400 行禁止提交
  • 业务逻辑超过 50 行必须提取为 composable
  • Composable 必须以 use 开头,返回对象必须解构友好
  • 每个 API 模块对应一个文件(src/api/{module}.ts
  • 禁止在 Controller 中操作 Model必须经过 Service + Repository
  • 禁止在 Model 中调用 Service保持 Model 纯净)
  • 禁止跨业务域直接引用内部组件(通过公开 index.ts 导出)
  • 事件名必须是:{域} + {动作的过去时} (如 OrderCreated)
  • 每次 PR 提交前运行 eslint --rule import/no-cycle 检查循环依赖
  • 前端导入方向必须单向:views → composables → stores → api → utils
  • 通用组件 (components/core/) 禁止依赖业务状态 (stores/)