找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2051

积分

0

好友

266

主题
发表于 12 小时前 | 查看: 0| 回复: 0

OpenSpec 变更工作流程图

1. 引言

OpenSpec 不仅仅是一个工具,它代表了一种以规格(Spec)为中心的工程实践与完整的方法论。其核心目标是通过结构化的文档与自动化工作流,来提升复杂系统从设计到交付整个周期的确定性与可验证性。本文将通过构建一个小型电商网站的完整案例,为你演示如何将 OpenSpec 应用于真实项目:从架构设计、模块划分、接口定义,到单元测试、集成测试乃至性能验证的全流程。理解了 OpenSpec 在 系统架构 设计中的价值后,你就能更好地管理复杂项目。

图中的流程仅供参考,更多详细信息可以访问其官方网站:https://openspec.dev/

案例代码仓库:https://github.com/ForceInjection/OpenSpec-practise


2. 方法论与 OpenSpec 核心概念

OpenSpec 远不止一套文档模板,它倡导的是一种 “规格驱动开发(Spec-Driven Development)” 的工程文化。它坚持“规格即源头”,确保代码与测试永远跟设计对齐,从而解决传统开发中“文档永远跟不上代码”的老大难问题。

2.1 核心哲学

OpenSpec 的设计遵循四大原则,让实践过程更灵活、高效:

  • 流动而非僵化 (Fluid not rigid):不设强制审批关卡,按实际需求创建和更新文档。
  • 迭代而非瀑布 (Iterative not waterfall):在构建过程中不断学习和修正规格,而非一次性定死。
  • 简单而非复杂 (Easy not complex):以轻量级的方式启动,避免繁琐的流程拖累团队。
  • 存量优先 (Brownfield-first):不仅适用于全新的“绿地项目”,更能通过其 Delta(增量变更)机制,平滑地接入现有的、复杂的代码库。

2.2 目录结构与单一事实来源

为了清晰区分“当前已确定的状态”和“正在进行中的变更”,OpenSpec 将项目状态划分为两个核心区域:

  • Source of Truth (openspec/specs/)
    这里是系统 当前 真实行为的权威描述。所有已发布的功能特性,都必须在此处有对应的规格定义。目录结构通常按业务领域(Domain)划分,例如 auth/spec.mdpayment/spec.md
  • Proposed Changes (openspec/changes/)
    这里是所有 进行中 的变更。每个变更都是一个独立的文件夹(例如 openspec/changes/add-login/),里面包含了该变更的完整上下文信息:
    • Proposal (proposal.md):阐述 Why & What。说明变更的背景、意图与范围。
    • Design (design.md):定义 How。包括技术方案、架构图与数据流。
    • Specs (Deltas):记录 Changes。这是对 openspec/specs/ 中现有规格文件的修改草案。
    • Tasks (tasks.md):拆解 Steps。列出具体的实施步骤与验收标准。

当一个变更开发完成并通过验收后,通过归档(Archive)操作,其 Delta Spec 会合并回主 Spec(openspec/specs/),从而形成新的事实来源。

2.3 工作流与 CLI 工具

OpenSpec CLI (openspec) 工具串联了从设计构思到最终交付的完整流程,并且天然支持人类开发者与 AI Agent 的协同工作:

  • 初始化 (init):一键生成标准的 OpenSpec 目录结构,配置 .openspec 环境。
  • 浏览与查看 (list / view):快速检索项目现有的变更与规格,支持 JSON 格式输出,便于 AI 工具解析。
  • 校验 (validate):基于预设的 Schema 检查文档结构的合法性与完整性,确保 Spec 格式严谨无误。
  • 归档 (archive):将已完成的变更移入专门的归档区,保持当前工作区的整洁与聚焦。

2.4 验证与可观测性

  • 结构化校验 (Validation)
    OpenSpec 引入了如 Zod 这样的校验机制,确保 Spec 文档不仅是纯文本,更是符合 Schema 定义的结构化数据。这为后续自动生成测试用例(Test Case Generation)铺平了道路。
  • 遥测 (Telemetry)
    工具内置了基于 PostHog 的匿名遥测功能(可选开启),用于收集如 command_executed 这类命令执行数据,帮助团队分析工具的使用频率和识别流程中的瓶颈。其设计严格遵循隐私原则,不收集命令参数、IP地址或任何业务内容,并支持通过设置环境变量 OPENSPEC_TELEMETRY=0 来完全关闭此功能。

3. 迭代流程总览

OpenSpec 的迭代流程紧紧围绕“规格优先”的理念展开,但它摒弃了传统瀑布模型中繁琐的审批环节,倡导灵活、敏捷的协作方式。

3.1 目标与范围 (Proposal)

在动手写第一行代码之前,首先通过 proposal.md 文件明确 Why(为什么做)What(做什么)

  • 业务目标:例如“构建一个最小可用的电商下单流程”。
  • 非功能性指标 (SLO)
    • p99 接口延迟 < 100ms
    • 支持 50 RPS(每秒请求数)的并发
    • 使用内存数据存储(演示阶段)

3.2 规格初始化 (Schema)

使用 CLI 工具快速初始化项目结构(如果你直接使用 examples/ecommerce-mini 源码,此步骤已完成,可跳过):

openspec init --tools none

openspec/changes/ 目录下创建一个新的变更集(例如 v1-mvp),并准备以下核心文件:

3.2.1 proposal.md (宏观意图)

这是项目的总纲,清晰定义了初衷和成功标准。

# Proposal: Ecommerce Mini MVP

## Intent
构建一个最小可用的电商系统,演示 OpenSpec 端到端流程。

## Goals (SLO)
- **Latency**: 核心接口 p99 < 100ms。
- **Quality**: 核心逻辑测试覆盖率 > 80%。

## Scope
- **In Scope**: Catalog, Cart, Order, User, Memory Storage.
- **Out of Scope**: Search, Recommendation, Payment Gateway.

3.2.2 其他核心文件

  • design.md: 系统设计草案,包含架构图和数据流。
  • specs/: 具体的接口定义与数据模型规格。
  • tasks.md: 拆解后的具体开发任务清单。

3.3 架构与系统设计 (Design)

基于 Proposal 定下的基调,在 design.md 中进一步确定技术方案:

  • 边界:明确 Catalog(商品)、Cart(购物车)、Order(订单)、User(用户)等各个模块的职责与界限。
  • 数据流:绘制关键业务路径,如“用户浏览 -> 添加购物车 -> 提交订单 -> 模拟支付”。
  • 接口:定义 RESTful API 的 URL 结构、HTTP 方法及请求响应格式。

3.4 规范驱动实现 (Implementation)

这是 OpenSpec 理念的实践核心——代码是对规格的精确映射

  • 领域层 (domain/):直接实现 Spec 中定义的数据模型和业务规则。
  • 接口层 (http/):直接实现 Spec 中定义的 API 端点。
  • 测试层 (__tests__/):直接验证 Spec 中列出的验收场景(Scenarios)。

3.5 验证与度量 (Verification)

  • 自动化测试:运行单元测试与集成测试,确保每一行实现都符合 Spec 的预期。
  • 基线度量:在开发阶段就运行性能基准测试(例如 performance.spec.js),确保系统满足既定的 SLO(服务水平目标)。

3.6 归档与沉淀 (Archive)

v1-mvp 变更开发完成并通过所有验收后,运行 openspec archive 命令。这个操作会将变更集中的 Spec 修改(Deltas)合并到主分支(openspec/specs/),使其成为系统最新的、单一的事实来源。


4. 案例背景:小型电商网站

4.1 核心域与上下文

本案例将构建一个名为 ecommerce-mini 的微型电商系统,它包含五个核心业务上下文:

  • Catalog (商品): 负责管理商品信息与库存。
  • User (用户): 处理用户身份识别与认证。
  • Cart (购物车): 临时存放用户打算购买的商品。
  • Order (订单): 交易的核心单据,管理订单状态的完整流转。
  • Payment (支付): 模拟资金结算流程。

4.2 简化假设 (Constraints)

为了将焦点完全放在 OpenSpec 的流程演示上,本项目在工程实现上做了一些折中和简化:

  • 数据存储:仅使用内存 Map 或本地 JSON 文件,不引入外部数据库。
  • 单体架构:所有业务模块运行在同一个 Node.js 进程中,通过模块导入方式进行通信。
  • 环境依赖:核心部分仅依赖 Node.js (v20+),实现零 npm 生产依赖(生产级扩展特性除外)。

4.3 非功能性目标 (SLO)

  • 延迟:核心 API (如 GET /products, POST /orders) 的 p99 延迟 < 100ms。
  • 可靠性:订单数据在服务重启后不应丢失(这需要通过持久化扩展来实现)。
  • 质量:核心业务逻辑的代码测试覆盖率 > 80%。

5. 架构设计

5.1 分层架构

项目采用经典的四层架构,确保清晰的关注点分离:

层级 目录 (src/) 职责 依赖方向
接口层 http/ 处理 HTTP 请求/响应,参数解析,鉴权,响应格式化 -> Application
应用层 services/ 用例编排(Orchestration),例如“下单”涉及扣库存、清购物车等多个步骤 -> Domain, Repo
领域层 domain/ 纯净的业务实体(定义在 types.ts)与业务逻辑,无任何外部依赖 None
基础设施 repo/, persist/ 数据持久化的具体实现(内存/文件) Implementation Detail

5.2 边界与依赖规则

  • 严格单向依赖:HTTP 层 -> Service 层 -> Domain 层。
  • 依赖倒置:Service 层定义 Repository 接口,由 Infrastructure 层提供具体实现(本示例为简化直接调用)。
  • 数据隔离:模块间不允许直接访问对方的数据存储,必须通过公开的 Service 接口进行调用。

5.3 数据流概览

以“用户下单”这个核心场景为例,描述其数据流转过程:

  1. User 发起 POST /api/orders 请求。
  2. HTTP Layer 解析请求头中的 Token,验证用户身份。
  3. Order Service 接收请求并协调业务:
    • 调用 Cart Service 获取当前用户的购物车商品列表。
    • 调用 Catalog Service 原子性地扣减相应商品的库存(这里是事务一致性的边界)。
    • 计算订单总价,生成订单实体。
  4. Order Repo 将新生成的订单数据保存至存储。
  5. HTTP Layer 返回 201 Created 状态码及订单详情。

6. 系统设计

6.1 接口协议

采用广泛使用的 RESTful JSON 风格。

  • URL 规范:资源使用复数形式,例如 /api/products
  • 状态码
    • 200 OK: 查询或修改成功。
    • 201 Created: 资源创建成功。
    • 400 Bad Request: 业务校验失败(如参数错误)。
    • 401 Unauthorized: 用户未登录或 Token 无效。
    • 409 Conflict: 资源状态冲突(如重复下单、库存不足)。

6.2 数据模型

在 OpenSpec 实践中,我们首先在 specs/domain/spec.md 中定义模型。为了方便理解,这里使用 TypeScript Interface 语法作为通用描述语言:

Spec 定义 (specs/domain/spec.md):

interface Product {
  id: string; // 格式:prod_xxxx
  name: string;
  priceCents: number; // Integer, min 0
  stock: number; // Integer, min 0
}

代码实现 (src/domain/types.js / JSDoc):
由于本项目使用原生 JavaScript,我们通过 JSDoc 注释来实现对 Spec 的映射:

/**
 * @typedef {Object} Product
 * @property {string} id
 * @property {string} name
 * @property {number} priceCents
 * @property {number} stock
 */

6.3 错误处理

定义统一的错误响应结构,便于前端进行标准化处理:

{
  "code": "OUT_OF_STOCK",
  "message": "商品 [prod_123] 库存不足"
}

7. 模块详细设计

7.1 Catalog (商品域)

  • Capabilities:
    • listProducts(): 查询所有商品(演示阶段不做分页)。
    • getProduct(id): 根据ID查询商品详情。
    • deductStock(id, qty): 原子性扣减库存,需处理并发竞争(演示中简化为单线程锁)。

7.2 Cart (购物车域)

  • Capabilities:
    • addToCart(userId, item): 向指定用户的购物车中添加商品(增量更新)。
    • clearCart(userId): 用户下单后清空其购物车。
  • Storage: 使用内存 Map,Key 为 userId,Value 为 Cart 对象。

7.3 Order (订单域)

  • Capabilities:
    • createOrder(userId): 核心复杂逻辑,协调 Cart 与 Catalog 服务,完成下单。
    • payOrder(orderId): 处理支付,将订单状态从 PENDING 流转至 PAID

8. 接口设计 (Spec)

OpenSpec 的一大优势是使用 Markdown 编写兼具极高可读性与结构化的规格文档。以下是 openspec/specs/api/spec.md 文件的一个真实片段:

# API Specification

## Endpoints

### GET /api/products
获取所有商品列表。
- **Response 200**: `Product[]`

### POST /api/cart/items
添加商品到购物车。
- **Body**: `{ productId: string, quantity: number }`
- **Response 200**: Updated `Cart`

### POST /api/orders
结算购物车生成订单。
- **Body**: `{ userId: string }`
- **Response 201**: `Order`
- **Response 409**: Stock insufficient (库存不足)

注意:这里的 Product[]Order 引用了 domain/spec.md 中定义的数据模型,确保了整个规格定义的一致性。

9. 规范驱动实现 (Implementation)

这是 OpenSpec 理念落地的核心环节——编写代码的过程,就是对照规格进行精确映射的过程。开发者(或 AI)在编码时应始终打开相关的 Spec 文件作为唯一参考。

9.1 追踪矩阵 (Traceability)

我们可以建立如下映射关系,确保每一条 Spec 要求都有对应的代码实现和验证手段:

Spec 定义 (Requirements) 代码实现 (Implementation) 验证方式 (Verification)
POST /api/orders src/http/server.js (Route Handler) Integration Test
Response 409: Stock insufficient catch (e) { if (e.message === 'OUT_OF_STOCK') ... } Unit Test (Error Case)
Order.totalCents (Model) src/domain/types.js (Interface) TypeScript Compile / JSDoc Check
p99 < 100ms (SLO) performance.spec.js (Performance Test) CI Pipeline

9.2 目录结构映射

examples/
├── openspec/                 <-- 对应 Spec Source of Truth (共享规格)
│   └── changes/v1-mvp/...
├── ecommerce-mini/           <-- Node.js Implementation
│   └── src/
│       ├── domain/types.js   <-- 对应 Spec 中的 Data Models
│       ├── services/         <-- 对应 Spec 中的 Business Rules
│       ├── http/server.js    <-- 对应 Spec 中的 API Definitions
│       └── persist/          <-- 对应 Design 中的 Storage Strategy
└── ecommerce-mini-python/    <-- Python Implementation

9.3 代码实现示例

Controller 层 (src/http/server.js):

// 对应 Spec: POST /api/orders
if (pathname === "/api/orders" && req.method === "POST") {
  const body = await readJson(req);
  try {
    // 编排业务逻辑 (Orchestration)
    // 1. 检查购物车 (Rule: Cart Not Empty)
    // 2. 检查库存 (Rule: Stock Check)
    // 3. 创建订单
    const order = orderService.createOrder(body.userId);
    return sendJson(res, 201, order);
  } catch (e) {
    if (e.message === "CART_EMPTY")
      return sendError(res, "CART_EMPTY", "购物车为空", 400);
    if (e.message === "OUT_OF_STOCK")
      return sendError(res, "OUT_OF_STOCK", "库存不足", 409);
    throw e;
  }
}

10. 测试设计:验证规格

在 OpenSpec 的实践中,测试不是事后的补充,而是规格的可执行版本,是验证实现是否符合设计的最终手段。

10.1 单元测试 (src/domain/logic.spec.js)

针对领域层中的纯函数逻辑进行测试,例如金额计算、状态机流转。

  • Spec: “订单总价等于所有商品条目(单价 × 数量)之和”
  • Test: 使用 Node.js 原生的测试运行器
import { test } from "node:test";
import assert from "node:assert";
import { calculateTotal } from "./logic.js";

test("calculateTotal sums up item prices", () => {
  const items = [
    { priceCents: 100, quantity: 2 },
    { priceCents: 50, quantity: 1 },
  ];
  const total = calculateTotal(items);
  assert.strictEqual(total, 250);
});

10.2 集成测试 (integration.spec.js)

模拟真实用户的完整操作路径,验证多个模块间的协同工作是否正常。

  • 流程:模拟用户注册 -> 登录 -> 浏览商品 -> 加入购物车 -> 提交订单 -> 支付。
  • 运行方式node --test examples/ecommerce-mini/__tests__/integration.spec.js

10.3 性能基线 (performance.spec.js)

定义并验证在“系统设计”阶段提出的 SLO(服务水平目标)。

  • Spec: “下单接口的 p99 延迟应小于 100ms”
  • Test: 编写脚本并发发送大量请求,统计响应时间的分布情况。如果 p99 值大于 100ms,则测试标记为失败。

11. 示例代码操作手册

11.1 准备环境

  • Node.js: 需要安装 v20.0.0 或更高版本(因为使用了 node:testfetch 等新特性)。
  • Git: 用于克隆代码仓库和版本控制。
  • Editor: 推荐使用 VS Code 等现代化代码编辑器。

本项目核心部分没有 package.json 依赖(除少数开发工具外),这不仅展示了 Node.js 的原生能力,也极大降低了运行和体验的门槛。

11.2 运行开发版服务

开发版使用内存存储,服务重启后所有数据将重置。

# 启动服务
node examples/ecommerce-mini/src/http/server.js

# 在另一个终端运行完整的测试套件
node --test examples/ecommerce-mini/__tests__/

11.3 运行生产版服务

生产版开启了文件持久化、JWT鉴权与幂等性检查等高级特性。

# 启动生产服务 (默认端口 3002)
node examples/ecommerce-mini/src/http/server.prod.js

12. 生产级扩展实践

为了演示 OpenSpec 如何优雅地应对真实世界的复杂性,我们在 server.prod.js 中引入了三个常见的高级特性。

12.1 持久化存储 (src/persist/fileStore.js)

  • Spec 变更: 系统需要在服务重启后仍能保留用户和订单数据。
  • 实现:
    • 实现 FileStore 类,使用 fs.writeFileSync 进行原子化的 JSON 文件写入。
    • 服务启动时,从磁盘加载数据到内存 Map 中。

12.2 鉴权与安全

  • Spec 变更: 所有非公开的 API 接口必须携带有效的 Bearer Token 才能访问。
  • 实现:
    • 新增 POST /api/auth/login 登录接口。
    • 使用 HMAC-SHA256 签名手动生成 JWT(演示无第三方库的实现)。
    • 在 HTTP 层添加中间件,拦截并校验 Authorization 请求头。

12.3 幂等性 (Idempotency)

  • Spec 变更: 对于同一订单的重复支付请求,系统应返回完全相同的结果,且不产生重复扣款等副作用。
  • 实现:
    • 客户端需要在请求头中发送一个唯一的 Idempotency-Key
    • 服务端检查此 Key 是否已处理过:
      • 若存在,直接返回之前缓存的响应结果。
      • 若不存在,则执行业务逻辑,并将处理结果与此 Key 关联缓存起来。

12.4 可观测性

  • Spec 变更: 系统需要暴露一个 GET /metrics 端点,供监控系统(如 Prometheus)采集指标。
  • 实现:
    • 在内存中记录每个 API 路由的请求次数与处理耗时。
    • 将指标以 JSON 格式暴露,例如:{ "requests": { "/api/orders": 100 }, "latencies": { "p99": 12 } }

13. 结语

通过 ecommerce-mini 这个完整的案例,我们展示了 OpenSpec 如何贯穿一个项目从最初的“提案(Proposal)”到具备生产级特性的“交付(Production)”的全生命周期。

  • 文档即设计:结构化的 Spec 在动工前就澄清了思路,统一了认知。
  • 代码即映射:清晰的分层架构和“规格驱动”的理念,使得编码过程变得更具确定性。
  • 测试即验收:自动化、多层次的测试脚本为持续重构和演进提供了坚实信心。

希望这份实战指南能成为你在实际项目中应用 OpenSpec 方法论的得力助手。如果你对这类规范驱动开发或全栈实践有更多想法,欢迎到 云栈社区 与更多开发者交流探讨。




上一篇:嵌入式工程师选型指南:从场景到规格,如何挑选核心板?
下一篇:MEDUSA安全测试工具发布:集成74种扫描器与180+AI Agent规则
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-31 22:57 , Processed in 0.382080 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表