LikeC4 Dynamic Views 完全指南:用 DSL 绘制流程与时序图
2026/9/18 5:14:58 网站建设 项目流程

LikeC4 Dynamic Views 完全指南:用 DSL 绘制流程与时序图

【免费下载链接】likec4Visualize, collaborate, and evolve the software architecture with always actual and live diagrams from your code项目地址: https://gitcode.com/GitHub_Trending/li/likec4

动态视图(Dynamic View)是 LikeC4 中用于表达元素之间时间性交互与调用流的视图类型。它把 C4 模型中的静态关系"串"成一条条带方向的步骤,渲染为可动画的流程图(Flow Diagram),也可以切换为 UML 风格的时序图(Sequence Diagram)。本指南以 skills/likec4-dsl/references/dynamic-views.md 为骨架,结合本仓库的语法定义、校验器与测试用例,完整讲解 dynamic view 的语法、流程控制块、步骤属性、导航下钻、时序图变体与常见反模式,读完即可在.c4文件中写出可编译、可校验的复杂动态视图。

语法总览

动态视图写在views { ... }块内,以dynamic view <name> { ... }声明。与静态视图(view)不同,动态视图的核心不是谓词过滤,而是逐条显式书写的步骤(step)

views { dynamic view name { variant sequence // 可选:以 UML 时序图渲染 title "Flow title" description "Flow description" SOURCE -> TARGET "step title" SOURCE <- TARGET "return flow" // 流程控制块(每个都可带可选标题,可任意嵌套) parallel 'optional' { // 也支持:par、opt、loop、break SOURCE_1 -> TARGET_1 "parallel action 1" SOURCE_2 -> TARGET_2 "parallel action 2" } alt 'optional' { // 互斥分支容器 when 'condition' { SOURCE -> TARGET } else { SOURCE -> TARGET } } try 'optional' { SOURCE -> TARGET } catch 'optional' { SOURCE -> TARGET } finally 'optional' { SOURCE -> TARGET } } }

从语法定义(like-c4.langium)可以看到,DynamicView'dynamic' 'view' name=Id组成,其 body 是DynamicViewBody:先是一些视图属性(tags、title、description、variant 等),随后混排steps+=StepStatementrules+=DynamicViewRule(如includestyleautoLayout)。也就是说,动态视图内部可以同时存在步骤序列与静态视图规则,二者互相补充。

基本步骤

前向步骤

->表达从源到目标的调用/消息传递,标题会显示在箭头上:

dynamic view checkout-flow { customer -> frontend "opens cart" frontend -> backend "requests checkout" backend -> payment-service "initiates payment" payment-service -> bank "authorizes card" }

在语法层面,单个步骤对应 Step:source=ElementRef ('<-' | '->' | '-[rel]->') target=ElementRef,其中->是最常见的无类型箭头。注意元素引用既可以是简单名(customer),也可以是带层级限定名的引用(cloud.ui.mobile),后者在 e2e 用例 model.c4 中大量出现。

返回步骤

<-表示响应/返回流(语义上是从目标流回源):

dynamic view checkout-flow { customer -> frontend "opens cart" frontend -> backend "requests checkout" frontend <- backend "payment result" // 返回流 }
  • <-表示 response/return 流;
  • 渲染为虚/点线箭头,具体样式取决于主题;
  • 语义上明确指出流向是回到源。

语法中isBackward?='<-'正是为它设计的标记位(like-c4.langium)。校验器对返回步骤还有一个约束:stepSeries检查中,若链式表达式的起点是一个 backward step,会报Invalid chain after backward step(见 validation/dynamic-view.ts)。

链式步骤

把多次跳转写成一个复合表达式,语义上更紧凑:

dynamic view multi-hop { customer -> frontend "request" -> backend "forwards" -> db "query" }

语法上这对应StepSeriessource -> target1 -> target2 ...,每次跳转由(dotKind | '->' | '-[kind]->')连接(like-c4.langium)。当提示词明确要求链式语法时,应保持一整条链,而不是拆成多个顶层独立步骤(这也是文档 Quick anti-patterns 第一条)。

跳转局部属性体(hop-local body)

可以把属性体{ ... }只挂在某一次跳转(hop)上:

dynamic view checkout-flow { customer -> frontend -> api { technology 'HTTPS' navigateTo payment-detail } }

关键点:属性体只作用于它所在的那一次跳转。除非提示词允许,否则不要把块整体上移到整条链上。

流程控制块

步骤可以被分组进流程控制块(flow-control block)parallel只是其中一种,并非特例。每个块:

  • 在关键字后紧跟一个可选标题String),然后包裹一个{ ... }步骤体;
  • 可以任意深度嵌套——唯一的例外是parallel/par不能再嵌套在另一个parallel/par内。

块关键字映射表:

关键字含义
并行parallel/par块内步骤并发执行
可选opt可能被跳过的步骤组
循环loop会重复执行的步骤组
中断break打断外层流程(如退出 loop)
分支容器alt一组互斥分支的容器
分支(alt 内)when/if/elsealt的某一分支(ifwhen的别名)
异常处理try/catch/finally正常路径 + 可选错误处理

语法上这些块统一建模为SubflowStepkind: 'opt' | 'par' | 'parallel' | 'loop' | 'when' | 'if' | 'else' | 'break'),见 like-c4.langium。try则是独立的TryStepTryBlock | CatchBlock | FinallyBlock),通过FinallyBlockCatchBlockTryBlock的嵌套链实现trycatch?finally?的固定顺序。

Parallel ——parallel/par

块内步骤并发执行,常用于一个源扇出到多个目标:

dynamic view fan-out { backend -> cache "write to cache" parallel { backend -> db "save record" backend -> audit-log "log event" backend -> notification-service "send notification" } }
  • par { ... }parallel { ... }的完全别名;
  • 渲染为时间对齐的水平布局;
  • 不可嵌套parallel { parallel { ... } }(或混用par)是校验错误Nested parallel blocks are not allowed;但parallel可以放在opt/loop/try/alt分支内部;
  • 当一个提示词只要求一次扇出时,把兄弟动作放在同一个parallel { ... }块里,不要拆成多个单步块。

这一限制在源码中有明确校验:subflowStep检查器判断kind === 'par' || kind === 'parallel'且其容器同为并行块时直接报错(validation/dynamic-view.ts),对应的测试见 views-dynamic-blocks.spec.ts 与 views-dynamic.spec.ts。

Optional ——opt

一组可能被跳过的步骤:

opt 'if not cached' { api -> db 'load and cache' }

Loop ——loop

一组会重复执行的步骤:

loop 'until success' { api -> auth 'retry authentication' }

Break ——break

打断外层流程(典型场景是退出loop):

loop 'poll for result' { api -> db 'check status' break 'when ready' { api -> web 'return result' } }

Alternatives ——altwhen/if/else

alt是互斥分支的容器,其直接子节点必须是分支——whenifelse(各自可带可选标题):

alt { when 'authorized' { app -> api 'requests data' } else 'not authorized' { app -> customer 'shows login' } }
  • ifwhen的别名,两者都是分支关键字;
  • when/if/else只能出现在alt内部。放在顶层或loop/parallel/opt内是校验错误:"when" alternative branch must be inside "alt"
  • 把非分支块(loopoptparalleltry)作为alt直接子节点也是校验错误:"loop" can not be used as an alternative branch。需要嵌套时,把这类块放进某个when/else分支内部。

语法上AltSteps定义为'alt' title? '{' branches+=SubflowStep* '}',而校验器通过isAltSteps(el.$container)判断分支是否处于alt中(validation/dynamic-view.ts),测试覆盖见 views-dynamic-blocks.spec.ts。

Try / catch / finally

建模"正常路径 + 可选错误处理"。catchfinally均可选,但顺序固定:trycatch?finally?

try { api -> db 'query' } catch 'on failure' { api -> web 'shows error' } finally { api -> api 'release resources' }
  • try单独、try { } catch { }try { } finally { }try { } catch { } finally { }都是合法组合;
  • 没有前置trycatch,或finally之后的catch,都是校验错误;
  • try不能作为alt的直接子节点(只有分支可以)——请把它放进when/else分支内。

上述四种合法组合在 views-dynamic-blocks.spec.ts 中有对应测试,非法用例(裸catchcatch置于finally之后、try直接放进alt)也都用toBeInvalid断言验证。

嵌套

除了parallel不能套parallel,其他块可以任意组合嵌套:

dynamic view resilient-sync { variant sequence loop 'until synced' { try { alt { when 'online' { web -> api 'sync changes' } else 'offline' { opt { web -> web 'queue locally' } } } } finally { web <- api 'ack' } } }

e2e 仓库中 views.c4 的flow-control-1视图是一个真实的综合样例:alt内含when/elseelse里再套loop;随后try { parallel { ... } } catch { ... } finally { ... },再接opt与链式返回步骤,几乎用全了所有控制块。

步骤属性(Step Properties)

给步骤挂一个{ ... }body 可以附加更多元数据:

dynamic view with-notes { customer -> frontend "view product" { title "View Product Detail" technology "HTTPS" description "Customer opens product page" } frontend -> backend "fetch product data" { notes """ Includes: - Product info - Pricing - Available inventory """ } backend -> db "query" { metadata { latency "50ms" cached false } } }

步骤体内可用属性:

  • title— 备用或更长的标题
  • technology— 所用技术/协议
  • description— 详细描述
  • notes— Markdown 格式的注释
  • metadata— 自定义键值对

语法上,步骤的custom=CustomRelationProperties?挂的是关系级属性集(like-c4.langium),其中包含navigateTo、字符串属性、notationnotes、关系样式属性与multiple。测试 views-dynamic.spec.ts 验证了步骤级descriptioncolornotationtitlenotes(含多行 Markdown)均合法。

导航与下钻(Navigation & Drill-down)

从步骤链接到另一个视图:

dynamic view high-level { customer -> frontend "browse" frontend -> backend "request data" { navigateTo>dynamic view checkout-sequence { variant sequence // 切换到时序图渲染 customer -> frontend "click checkout" frontend -> backend "POST /checkout" backend -> payment-service "charge card" payment-service <- bank "authorization" backend <- payment-service "charge approved" frontend <- backend "200 OK" customer <- frontend "show confirmation" }
  • variant sequence告诉 LikeC4 按时序图渲染;
  • 时间线自上而下流动(而不是流程图的自左向右);
  • 参与者(actor)成为 lifeline;返回箭头为虚线。

语法上由DynamicViewDisplayVariantProperty: key='variant' value=DynamicViewDisplayVariantValue定义(like-c4.langium),且variant属性只能在动态视图内部出现——校验器dynamicViewDisplayVariant会检查其容器必须是DynamicViewBody,且取值只能是diagramsequence(validation/dynamic-view.ts)。e2e 中的 model.c4 有variant sequence的真实用例。

流程图与时序图渲染对比

维度流程图(Flow Diagram)时序图(variant sequence)
布局自左向右或自上而下自上而下的 lifeline
返回箭头样式随主题变化标准虚线
并行的表现水平对齐基于时间、带间距
最佳场景系统交互消息交换协议

返回箭头模式(Response Arrow Patterns)

当提示词要求"返回箭头"时,优先用<-步骤表达响应,而不是凭空发明额外的前向步骤:

dynamic view checkout-flow { customer -> frontend -> api customer <- frontend <- api // 响应链优先用 <- 保持对称 }

不要写成:

dynamic view checkout-flow { customer -> frontend -> api api -> frontend "returns" // 错误:不是返回箭头(缺少对称性) frontend -> customer "returns" }

常见模式

扇出 + 返回

dynamic view fan-out-return { backend -> cache "check" backend -> db "if miss" backend <- db "result" backend -> client "send" }

顺序:先发给多个目标,再各自收集(结果不同时)响应。注意这里"check 后 if miss 再 query"如果目标是表达并发,应改用parallel;文档 dynamic-views.md 中with-cache示例展示了frontend -> cache "check"/cache -> backend "if miss"的缓存旁路写法。

管道 / 分阶段流程

dynamic view>dynamic view payment-decision { customer -> frontend "enter amount" frontend -> backend "validate" alt { when 'within limit' { backend -> payment-service "charge" backend <- payment-service "result" } else 'over limit' { backend -> frontend "rejected" } } }
  • 需要"无 else 的 if"(一组可跳过的步骤)时用opt;需要成功 vs 失败路径时用try/catch
  • 当不值得写一个完整分支块时,步骤上的notes仍是记录决策逻辑的轻量方式。

常见反模式

反模式问题正确做法
嵌套parallel { parallel { ... } }Nested parallel blocks are not allowed把所有并发步骤放进一个parallel {}
when/if/else出现在alt之外alternative branch must be inside "alt"alt { ... }包住分支
loop/opt/parallel/try直接作为alt子节点can not be used as an alternative branch把它们嵌套在when/else分支内部
没有try就写catch,或finally之后写catchtry/catch/finally 顺序非法保持trycatch?finally?
单个视图步骤过多图变得不可读navigateTo拆分成子视图
<-表达并列动作语义上暗示返回,破坏时序语义用前向->parallel分组
需要 UML 时序图却忘了variant sequence渲染结果不对需要时序图时加上variant sequence

动态视图中的谓词过滤

动态视图不使用谓词来生成或过滤交互步骤——步骤必须逐条显式列出。

但仍可以用普通的 include 谓词添加不参与步骤的上下文元素:

dynamic view critical-flow { frontend -> api "request" api -> db "query" include cloud.* where tag is #critical include cloud.* where metadata.region is "eu" } dynamic view with-cache { // 带缓存层的备选流程 frontend -> cache "check" cache -> backend "if miss" }

语法上这对应DynamicViewIncludePredicate: 'include' exprs=Expressions(like-c4.langium),与静态视图的include/exclude谓词规则(ViewRulePredicate)不同——动态视图只支持include方向。e2e 用例 model.c4 与 views.c4 中都有include+ 谓词/样式配合步骤使用的实例。

典型应用场景

文档 dynamic-views.md 列出的常见场景:

  • 认证流程— customer → login → auth-service → database
  • CQRS 模式— command → service → write-db(query-handler → read-db 用parallel
  • Webhook 回调— external-system → api(内部parallel,配合navigateTo service-webhook-handler下钻)
  • Pub/Sub 流— publisher → message-bus → consumer(多个订阅者用parallel

写作时的精确性要点:

  • 提示词要求链式语法时,保持一条复合链,不要改写成多个顶层独立步骤;
  • 提示词明确要求返回箭头时,用<-而不是前向箭头;
  • 一次并行扇出只用一个parallel块,不要拆成多个;
  • 不要省略提示词要求的 hop 局部 token(如technologynavigateTo);
  • when/if/else只能直接放在alt内;
  • loop/opt/parallel/try不能作为alt的直接子节点;
  • parallel内不能再嵌套parallel(唯一不可嵌套的块);
  • 严格遵守trycatch?finally?的顺序。

底层实现参考

想深入理解 dynamic view 的编译与校验链路,可以在本仓库中继续阅读:

  • 语法定义:packages/language-server/src/like-c4.langium(DynamicView声明)、L648-L812(步骤与流程控制块语法);
  • 校验逻辑:packages/language-server/src/validation/dynamic-view.ts(并行嵌套、alt 分支归属、try/catch/finally 顺序、variant 取值);
  • 单元测试:packages/language-server/src/tests/views-dynamic.spec.ts、views-dynamic-blocks.spec.ts;
  • 视图计算:packages/core/src/compute-view/dynamic-view/compute.ts 与 computeFlow.ts;
  • 端到端样例:e2e/src/likec4/model.c4(含paralleloptvariant sequencenavigateTo)、e2e/src/likec4/views.c4(alt/loop/try/parallel综合嵌套)。

掌握了这些语法、校验规则与反模式,你就能在 LikeC4 中稳定地产出既符合语言规范、又能在编辑器(LSP)中零报错的动态视图与时序图。

【免费下载链接】likec4Visualize, collaborate, and evolve the software architecture with always actual and live diagrams from your code项目地址: https://gitcode.com/GitHub_Trending/li/likec4

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

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

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

立即咨询