Unleash 领域语言(Domain Language)规范:以 Change Request 为例的统一术语体系解析
2026/9/15 3:37:21 网站建设 项目流程

Unleash 领域语言(Domain Language)规范:以 Change Request 为例的统一术语体系解析

【免费下载链接】unleashOpen-source feature management platform项目地址: https://gitcode.com/GitHub_Trending/un/unleash

导读

本文基于 domain-language.md 这份架构决策记录(ADR),系统讲解 Unleash 开源特性管理平台中"领域语言(Domain Language)"的治理思路与落地实践。领域语言是团队在代码库、API、UI 与文档之间共享的一套统一术语,它决定了"同一件事在不同地方应该叫同一个名字"。通过本文,你将掌握 Unleash 针对 Change Request(变更请求)功能定义的核心术语(Change request / Change / Changes / Discard / Pending / Closed),理解这些术语如何与源码中的状态机、数据模型和查询逻辑一一对应,并能将这套方法论复用到你自己项目的术语治理中。

一、背景:为什么一个开源项目需要"领域语言"

在 domain-language.md 的 Background 一节中,Unleash 团队明确说明了引入领域语言的原因:

在代码库中,我们看到了定义一套领域语言的需求,用来统一指称功能与方法,保证整个代码库的一致性。

随着代码库规模不断增长(当前仓库包含数百个服务、存储层与路由模块),同一概念很容易出现多种叫法:开发者在后端叫 "change request",前端组件里叫 "edit request" 或 "proposal",数据库字段里又叫 "state"。这种术语漂移会直接导致:

  • 跨模块沟通成本上升,Pull Request评审中反复争论命名;
  • 搜索代码时漏掉关键实现(同一概念的关键词不统一);
  • OpenAPI 生成的接口与前端类型定义产生语义偏差;
  • 新成员上手成本变高,难以判断某个词是否是该领域的官方术语。

领域语言(Domain Language)正是为了解决这一问题而设立的统一词汇表,它是代码库内部的一份"活词典",随功能演进持续扩充。

二、决策:每个功能维护自己的领域语言

ADR 的 Decision 一节给出了核心决策:

我们决定,对我们开发的功能使用相同的领域语言。每个功能都应有属于自己的领域语言,以保持整个代码库的一致性。

这段决策包含两个层次的含义:

  1. 全局一致:同一概念在代码库的任何位置(后端服务、数据库列、API 参数、前端组件、测试、文档)都必须使用同一术语;
  2. 按功能域划分:不同的功能模块各自维护一份术语表(例如 Change Request 有 Change Request 的术语,Segment、Release Plan 等模块可以有自己的术语),避免用一套全局词汇生硬套用所有场景。

这是一种"分而治之"的术语治理模式:既保证单个功能域内部严格统一,又允许不同功能域使用各自贴切的词汇。

三、Change Request 领域语言:核心术语逐条解析

ADR 文档主体用列表形式定义了 Change Request 功能域的六个核心术语,这是全文最重要的部分,逐条展开如下。

3.1 Change request(变更请求)

Change request:指变更请求的整体数据结构(overarching data structure)。一个变更请求包含若干 changes(变更),可以被批准(approved)或拒绝(rejected)。

在 Unleash 中,Change Request 是让特性开关(feature flag)的修改(如启用/禁用开关、调整策略、修改 Segment 引用)走审批流程的机制,常用于生产环境的变更管控。它是一个容器型实体,自身不直接代表某个具体改动,而是承载一组改动及其生命周期状态。

从源码看,该实体在数据库中以change_requests表存储,例如 feature-search-store.ts 中通过change_requests AS cr关联查询,并用cr.idcr.statecr.environment等字段描述其身份与状态。OpenAPI 侧也有对应 schema(如feature-environment-schema.ts中提及 change request 列表的语义)。

3.2 Change(变更)

Change:指变更请求中的单个变更。

例如"把 production 环境中 feature A 的 flexibleRollout 策略的 rollout 从 50% 调整为 100%"就是一条 change。源码中的addChangeRequestChange正是向某个 change request 追加单条 change 的落库操作,见 change-request-segment-usage-read-model.test.ts,其中包含updateStrategy这类单条变更动作及其完整 payload。

3.3 Changes(变更集合)

Changes:指变更请求中一组变更的集合。

一个 change request 内部可以有零到多条 change,Changes就是这组变更的统称。在数据模型上,单条 change 通过change_request_id外键归属到某个 change request,查询时以聚合(aggregation)方式组织,例如 feature-search-store.ts 中array_agg(distinct cre.change_request_id) AS change_request_ids就是把属于同一特性、同一环境的 change request id 聚合成数组。

3.4 Discard(丢弃)

Discard:用于删除某个变更请求中的单条变更,或整体丢弃整个变更请求。

Discard 是"不采纳改动"的动作语义,它有两种粒度:

  • 丢弃单条 change:从 change request 中移除某一条改动;
  • 丢弃整个 change request:取消整个变更请求。

在状态机的落地上,整体丢弃通常对应Cancelled(已取消)状态;而单条 change 的丢弃则是对集合内元素的删除操作。之所以用 "discard" 而非 "delete"/"cancel",是为了与"删除功能开关"、"取消部署"等其他领域的动作语义区分开。

3.5 Pending(待处理)

Pending:指尚未被应用(applied)或丢弃(discarded)的变更请求,即处于以下三种状态之一:

  1. Draft(草稿)
  2. In review(评审中)
  3. Approved(已批准)

Pending 是领域语言中的状态分组概念,它把三个"仍在生命周期中、还可以继续演进"的状态归为一类。源码中大量查询正是以"是否为 Pending/Active"为过滤条件:

  • segment-store.ts 在查询 Segment 在哪些活跃变更请求中被使用时,排除['Applied', 'Rejected', 'Cancelled'],剩余即为 Pending 类状态;
  • feature-search-store.ts 同样以whereNotIn('cr.state', ['Applied', 'Cancelled', 'Rejected'])来圈定"可操作的变更请求";
  • sql-change-request-segment-usage-read-model.ts 的 SQL 写法WHERE cr.state NOT IN ('Applied', 'Cancelled', 'Rejected')与上述逻辑完全一致。

3.6 Closed(已关闭)

Closed:指已经被应用或取消、不能再被修改的变更请求。状态为Applied(已应用)或Cancelled(已取消)的变更请求均视为 Closed。

Closed 是 Pending 的互补分组:一旦变更请求进入AppliedCancelled,它就进入终态,不再接受任何修改。

四、源码中的状态全集:领域语言与状态机的对应

ADR 文档定义了 Pending(Draft / In review / Approved)与 Closed(Applied / Cancelled)两组状态,而仓库源码与测试进一步给出了更完整的状态全集。从 change-request-segment-usage-read-model.test.ts 的参数化测试可以看出,实际代码中出现的状态还包括:

状态归属分组源码证据(测试断言"是否活跃")
DraftPending(活跃)该变更请求中的改动会被计入活跃结果
In reviewPending(活跃)同上
ScheduledPending(活跃)已排期、尚未执行,仍视为活跃(ADR 未单列,为源码补充状态)
ApprovedPending(活跃)已批准但未应用,仍可被计入活跃结果
RejectedClosed(非活跃)测试断言返回空结果
CancelledClosed(非活跃)同上
AppliedClosed(非活跃)同上

说明:Scheduled(已排期)与Rejected(已拒绝)并未出现在 ADR 文档中,但仓库的测试与 SQL 查询证实它们存在于实际状态机中。从实现看,Scheduled与 Draft/In review/Approved 一样属于"尚在生命周期中"的活跃状态,而Rejected与 Applied/Cancelled 一样属于终态。ADR 作为"活文档",其术语表会随功能演进持续扩充——这正是文档开头"growing list"(不断增长的列表)的题中之义。

同时,OpenAPI 侧 feature-environment-schema.ts 对"可操作的变更请求"也给出了与源码一致的语义注释:"尚未被 Cancelled、Rejected 或 Approved 的变更请求列表"(Experimental 字段)。

五、领域语言如何驱动实际业务逻辑:访问控制与绕过

领域语言不仅是命名规范,它还直接映射为业务规则。以 Change Request 访问控制为例,change-request-access-read-model.ts 定义的读模型接口清晰地体现了术语驱动的领域逻辑:

  • canBypassChangeRequest(project, environment, user?):判断某用户是否可以绕过该项目的变更请求流程(即不经审批直接改);
  • canBypassChangeRequestForProject(project, user?):项目维度的绕过能力判断;
  • isChangeRequestsEnabled(project, environment):判断某项目某环境是否启用了变更请求;
  • isChangeRequestsEnabledForProject(project):项目维度的启用状态判断。

在这套逻辑中,"变更请求是否启用"、"用户能否绕过"与"变更请求处于何种状态"共同决定了用户的写操作是否需要进入审批流。领域语言中的 Pending / Closed 分组正是这类规则落地的公共词汇基础:只有 Pending 状态的变更请求才参与审批、才可能被 Approve/Discard。

六、如何为你的项目建立领域语言

结合 domain-language.md 与 Unleash 的实践,可以提炼出一套可复用的术语治理方法:

  1. 用 ADR 固化词汇表:以架构决策记录的形式把术语定义写入仓库,随代码一起评审、一起演进,而不是散落在口头约定或聊天记录里;
  2. 按功能域组织:每个核心功能模块维护自己的术语小节(如本文的 "Change requests domain language"),先定实体名词(如 Change request),再定动作动词(如 Discard),最后定状态分组(如 Pending / Closed);
  3. 定义到"可判定"的粒度:每个术语都要给出明确判定标准,例如 Pending 必须能枚举出它包含的具体状态,而不是一句模糊的"还没结束";
  4. 与代码互相印证:术语定义后,要让数据库枚举值、OpenAPI schema、服务方法命名与之一一对应(Unleash 中whereNotIn('state', ['Applied', 'Cancelled', 'Rejected'])就是 Pending 判定在 SQL 层的投影);
  5. 保持"活文档"心态:文档明确标注为 "growing list",当状态机新增状态(如 Unleash 后来加入的ScheduledRejected)时,术语表应及时补充,避免文档与实现脱节。

七、总结

Unleash 的 domain-language.md 展示了一种轻量而有效的领域术语治理模式:以 ADR 为载体、以功能域为粒度、以状态枚举为落点,让"术语"成为代码、测试、API 与文档之间的事实连接点。在 Change Request 这一功能域中,Change request / Change / Changes / Discard / Pending / Closed 六个术语构成了完整的概念骨架,而仓库源码(feature-search-store.ts、segment-store.ts、change-request-access-read-model.ts)则以可运行的查询与测试验证了这套术语的真实语义。对于任何正在长大的代码库,这套"先定词、再写码、代码反哺词表"的方法论都值得借鉴。

【免费下载链接】unleashOpen-source feature management platform项目地址: https://gitcode.com/GitHub_Trending/un/unleash

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询