Sparkdo.Messaging.Visualization
3.0.0-preview.1
dotnet add package Sparkdo.Messaging.Visualization --version 3.0.0-preview.1
NuGet\Install-Package Sparkdo.Messaging.Visualization -Version 3.0.0-preview.1
<PackageReference Include="Sparkdo.Messaging.Visualization" Version="3.0.0-preview.1" />
<PackageVersion Include="Sparkdo.Messaging.Visualization" Version="3.0.0-preview.1" />
<PackageReference Include="Sparkdo.Messaging.Visualization" />
paket add Sparkdo.Messaging.Visualization --version 3.0.0-preview.1
#r "nuget: Sparkdo.Messaging.Visualization, 3.0.0-preview.1"
#:package Sparkdo.Messaging.Visualization@3.0.0-preview.1
#addin nuget:?package=Sparkdo.Messaging.Visualization&version=3.0.0-preview.1&prerelease
#tool nuget:?package=Sparkdo.Messaging.Visualization&version=3.0.0-preview.1&prerelease
Sparkdo.Messaging.Visualization
把冻结的消息图投影成稳定快照、算成确定性布局、再渲成 SVG 片段。只做数据与几何,不渲染宿主页面(那是 Sparkdo.Messaging.Visualization.Blazor 的事)。
| 项 | 值 |
|---|---|
| 包 | Sparkdo.Messaging.Visualization |
| 目标框架 | net11.0(由域内 Directory.Build.props 统一给,包本身不写 TFM) |
| 外部 NuGet 依赖 | 无;JSON 用 System.Text.Json 源生成 |
| 输入 | MessageBusGraph(只读公开面:Routes / ReceivePlans / Transports / DescribeTopology()) |
| 输出 | DiagramSnapshot(JSON)→ DiagramLayout → SVG 片段 |
| AOT / 裁剪 | 兼容:序列化走源生成,无反射、无动态代码 |
| 快照契约版本 | DiagramContract.Version = 1 |
管线
MessageBusGraph ──Project──▶ DiagramSnapshot ──Serialize──▶ JSON / ETag
│
└──Compute──▶ DiagramLayout ──Render──▶ SVG
四步都是纯函数:同一张图投影两次得到同样的快照;同一快照布局两次得到同样的坐标;同样布局渲两次得到同样的标记。排序全部在投影里做完,之后的步骤不再排。
什么时候用它
- 想在宿主里画依赖图、拓扑图或「消息都去哪了」的健康页:用
DiagramProjector+DiagramLayoutEngine+DiagramSvgRenderer。 - 想让前端自绘、或把图存进对象存储做 diff:只取
MessagingDiagramJson的输出,前端按快照字段画。 - 不合适:想直接拿到 React / 其他 DOM 组件(本包只出 JSON 与 SVG 字符串);想做运行时热更新(快照是按需投影的一帧,差异自己算)。
接线
using Sparkdo.Messaging.Visualization;
var snapshot = DiagramProjector.Project(graph, new DiagramOptions
{
ServiceName = "orders",
InstanceId = "pod-1"
});
var json = MessagingDiagramJson.Serialize(snapshot); // byte[],给响应体
var etag = MessagingDiagramJson.ComputeETag(json); // 强 ETag:引号 + 64 位小写 hex
var layout = DiagramLayoutEngine.Compute(snapshot);
var svg = DiagramSvgRenderer.Render(layout, "Sparkdo Messaging", selectedNodeId: "msg:urn:message:sparkdo.orders:order-placed");
graph 由 IServiceProvider.BuildMessagingAsync() 得到;本包不建图、不改图。
JSON 契约
- camelCase 属性名、两空格缩进、
null字段整段省略(JsonIgnoreCondition.WhenWritingNull)。 - 数组顺序是契约的一部分,投影时已排好:
transports按scheme序数排序,capabilities按名字序数排序,topology保留传输自报顺序,kind直接用TopologyEntityKind枚举名(Topic/Queue/Binding),不做大小写加工;messageTypes按identity序数排序,enclosed按身份序数排序;consumers按identityTypeFullName序数排序;routes.outbound/routes.inbound按(endpointAddress, messageTypeIdentity)序数排序。
- 没有自定义
JsonConverter:快照字段全是string/bool/ 列表,源生成的默认映射就够;设计文档 §4.4 里预留的三个 converter 因此省掉。 - 字符串按
System.Text.Json默认编码器输出:非 ASCII 字符写成\uXXXX转义(例如中文拓扑说明),这是稳定且合法的 JSON。 - ETag = 完整
SHA-256(json)的 64 位小写 hex,两边加双引号(强 ETag 形式,可直接当ETag响应头)。 - golden 守门:
test/.../DiagramSnapshot.golden.json是手工固定的快照(覆盖 host、多传输、拓扑空值、Saga 与双向路由)序列化后的冻结字节,测试逐字节比对;改契约字段或序列化口径时必须显式刷新它,顺便让 diff 可见。 - 确定性边界:同一张冻结图(或登记顺序相同的两次构建)投影得到的字节一致;拓扑实体组内保序不重排,所以「换登记顺序重建的图」在拓扑段可能不同——这是刻意的,自描述与可视化要给传输自报的原始顺序。
host来自部署参数,不参与「同一张图」的字节保证;测试夹具用默认(无 host)配置。
布局口径
一条传输一条泳道(按 scheme 序数排序),泳道内固定四列:消息类型 → 发布端点 → 接收端点 → 消费方。一条路由属于哪条泳道,按端点地址的 scheme 判定(地址文法见核心包 MessagingAddresses)。空泳道不出现,空快照得到 0×0 的空布局。
| 常量 | 值 |
|---|---|
| 消息类型节点 | 180 × 34 |
| 发布端点节点 | 200 × 34 |
| 接收端点节点 | 220 × 34 |
| 消费方节点 | 200 × 34 |
| 行距 | 10 |
| 列距 | 60 |
| 泳道标题高 | 40 |
| 泳道内边距 | 24 |
| 泳道间距 | 32 |
| 画布外边距 | 24 |
| 泳道底留白 | 24 |
- 节点 ID:
msg:{identity}、topic:{address}、recv:{address}、consumer:{name};跨泳道撞 ID 时依次退让为…@{scheme}、…#2。泳道 ID:lane:{scheme}。 - 边 ID:
e:{sourceId}>{targetId},去重后按(sourceId, targetId)排序。 - 边三类:
msg → topic(publish)、topic → recv(subscribe,要求同泳道且身份相同)、recv → consumer(subscribe)。 - 边是四点三折线:
(源右缘, 源中心Y) → (中点X, 源中心Y) → (中点X, 目标中心Y) → (目标左缘, 目标中心Y),中点X 取两段水平距离的一半。 - 节点按
(泳道, 列, 标签)排序输出。 - 这里的数值以实施合同为准;设计文档 §5.2 的一组数字是草图,本包不采用。
SVG 口径
- 输出片段(
<svg>…</svg>),不是 HTML 文档;类名统一mv-前缀,颜色与字体归宿主 CSS,渲染器不管主题。 - 入口是
Render(DiagramLayout layout, string title, string? selectedNodeId = null):title进根元素的<title>,selectedNodeId只标一个节点。 - 根元素带
viewBox/width/height/role="img";<defs>里两个箭头 marker(mv-arrow-publish/mv-arrow-subscribe)。 - 泳道:
<g class="mv-lane">,说明文字为{Caption} · {该泳道节点数};Caption为空时只写计数。 - 节点:
<a class="mv-node-link" href="?node={id}" data-node="{id}"><g class="mv-node" data-kind="{DiagramNodeKind 枚举名}">…<text class="mv-node-icon">字形</text><text class="mv-node-label">…</text><title>全文</title></g></a>;图标按种类单一映射(消息 ✉、发布端点 ▤、接收端点 ⇄、消费方 ⚙),改映射就等于改图例。不再输出mv-node-{kind}这类拼出来的类名。 - 所有文本与属性值经
HtmlEncoder.Default编码;标签超过 24 字符截断并加…,全文放进<title>。注意该编码器也把非 ASCII 写成数字实体(如…),这是合法 XML,也是测试断言的比对方式。 - 边:
<path class="mv-edge mv-edge-{publish|subscribe}" d="…">,三折线,拐角圆角半径 8(不超过相邻段长的一半;长度不够就不圆角;两段共线时退化成直线)。 - 选中节点在
<a>上追加mv-selected类,只作用于一个节点。 - 不输出脚本、不引外部资源;交互只是
<a href="?node={id}">的静态导航,由宿主的页面路由解释。
出证
dotnet build src\messaging\src\Sparkdo.Messaging.Visualization\Sparkdo.Messaging.Visualization.csproj
dotnet test src\messaging\test\Sparkdo.Messaging.Visualization.Tests
测试覆盖:投影字段与排序、宿主信息、JSON 形状与完整 ETag、golden 字节夹具、字节确定性(含登记顺序)、布局坐标与节点不重叠、多泳道与边不跨泳道、空快照、SVG 结构/编码/截断/图标/选中态。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net11.0 is compatible. |
-
net11.0
- Microsoft.Extensions.ObjectPool (>= 10.0.8)
- Sparkdo.Messaging (>= 3.0.0-preview.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Sparkdo.Messaging.Visualization:
| Package | Downloads |
|---|---|
|
Sparkdo.Messaging.Visualization.Blazor
Sparkdo Messaging 可视化的 Blazor 组件与宿主接线:服务端渲染 SVG 页面、快照 JSON 端点。 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0-preview.1 | 63 | 10/30/2026 |