ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Penpot 数据模型详解:从 Profile 到 Shape 的完整实体体系

Penpot 数据模型详解:从 Profile 到 Shape 的完整实体体系 Penpot 数据模型详解从 Profile 到 Shape 的完整实体体系【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot本篇技术指南围绕 Penpot 官方技术文档 数据模型说明 展开系统讲解设计文档File背后从用户Profile、团队Team、项目Project到页面Page与图层Shape的全链路概念模型并结合仓库源码与数据库迁移脚本揭示这些概念在common共享层、后端 SQL 与前端之间各自的真实表现形式。读完本文你将能够看懂 Penpot 的数据组织方式、理解概念实体在应用与数据库中的表现形式略有差异这一核心设计思想并学会把 UI 中的任何图层追溯到其数据与存储层级的依据。概览一套概念多种形态Penpot 是一套全栈开源设计平台其数据模型文档首先强调了一个关键前提本文描述的是概念模型conceptual data model。这些实体在不同运行环境中有着略微不同的物理表示——前端应用里是内存中的状态结构后端 RPC 调用中是以 JSON 形式传输的消息体而 SQL 数据库里则可能被拆分成多张关联表。但无论形态如何变化概念始终一致这是理解整个代码库数据流的关键线索。对应到仓库中这套概念模型最直接的实现证据位于共享的 Clojure(Script) 类型与工具层 common/src/app/common/types/ 目录下profile.cljc、team.cljc、project.cljc、file.cljc、page.cljc、component.cljc、container.cljc、shape.cljc、shape_tree.cljc等文件一一对应文档中出现的各个概念实体而概念模型的数据库落地形式则可从 backend/src/app/migrations/sql/ 中 160 个递增编号的 SQL 迁移脚本中溯源例如0002-add-profile-tables.sql、0003-add-project-tables.sql、0031-add-conversation-related-tables.sql、0063-add-share-link-table.sql等。原文档中的 UML 类图采用 PlantUML 基本标记绘制见 plantuml 类图说明下文将逐个拆解。用户、团队与项目组织结构的三级体系实体关系原文档给出了第一张 UML 图其核心关系链如下Profile与Team之间是多对多关系*-*一个用户可以隶属于多个团队Team组合*--多个ProjectProfile与Project、Profile与File之间也存在直接的多对多关联表示拥有者 / 参与者的授权关系Project组合多个FileFile之间以 libraries 关系互相引用即共享资源库一个File各自组合多个StorageObjectmedia_objects、CommentThreadcomment_threads与ShareLinkshare_linksCommentThread组合多个Comment。各实体职责Profile系统内任何用户的个人信息载体。用户归属于团队Team并可在其中创建项目。Team / Project / File团队内所有用户都可以看到团队内项目与文件同时任何项目和文件都至少有一个 owner 用户但也可与其他用户建立带不同角色的更多关系这套多对多授权关系在数据库层面由0028-add-team-project-profile-rel-table.sql引入的关系表支撑。共享库libraries文件File可以引用其他文件作为共享资源库即File * -- * File : libraries这条自关联。StorageObject代表存放在外部存储中的一个文件对象目前主要是图片与 SVG 图标未来可能扩展更多媒体类型它被嵌入到某个设计文件之中。CommentThread 与 Comment用户针对文件添加的评论体系评论以线程聚合。ShareLink包含一个 token、一个 URL 以及若干权限用于把文件分享给外部用户对应迁移脚本0063-add-share-link-table.sql引入的分享链接表。源码印证在共享类型层profile.cljc、team.cljc 与 project.cljc 分别定义了schema:profile、schema:team、schema:project等 malli schema本项目用sm/register!注册共享 schema供前后端共同校验。文档中Project 与 File 上至少有一个 owner、但可有多角色用户的描述与数据库迁移中多次出现的关系表team-profile-rel、project-profile-rel、team-project-profile-rel见0088-mod-team-profile-rel-table.sql、0089-mod-project-profile-rel-table.sql、0091-mod-team-project-profile-rel-table.sql相符——在关系型数据库中多对多关系正是通过这类中间关系表落地。值得注意的还有0031-add-conversation-related-tables.sql评论相关表、0033-mod-comment-thread-table.sql、0077-mod-comment-thread-table.sql、0136-mod-comments-mentions.sql等一长串针对评论体系的迭代迁移说明评论实体在实际演进中不断补充字段如提及 / mentions恰好印证了文档概念模型不变、物理形态持续演化的论述。File data设计文件的核心载荷实体关系File实体除了组织层面的元信息之外其主要内容存放在file data属性中。第二张 UML 图刻画了 file data 内部的结构File组合多个Pagepages而(File, Page)共同建模出PagesListFile组合多个Component(File, Component)建模出ComponentsListFile组合多个Color建模出ColorsListFile组合多个MediaItem建模出MediaItemsListFile组合多个Typography建模出TypographiesList。也就是说file data 中既包含页面Pages也包含文件级资源资产library assets——即 Components、MediaItems、Colors、Typographies 四类资源库。为什么把这些列表也建模成实体文档特别解释了一个看似过度设计的点页面和资产的列表本身也被建模为实体PagesList、ComponentsList、ColorsList 等。原因在于这些列表承载了大量函数与业务逻辑——例如排序、查找、索引、增删改时的联动等把它们抽象成独立实体可以让代码在只关心集合操作时复用同一套逻辑而不是每次都在多个容器里重复写遍历代码。源码印证这个设计在 file.cljc 中有着精确的 schema 对应(def schema:data [:map {:title FileData} [:pages [:vector ::sm/uuid]] ; 页面 id 的有序向量 [:pages-index schema:pages-index] ; id - page 的索引映射 [:options {:optional true} schema:options] [:colors {:optional true} schema:colors] [:components {:optional true} schema:components] [:typographies {:optional true} schema:typographies] [:plugin-data {:optional true} schema:plugin-data] [:tokens-lib {:optional true} schema:tokens-lib]])可以看到 file data 中pages是UUID 向量有序列表而pages-index是id → page 的映射索引——这正对应文档中 PagesList 实体的双重职责同理colors、components、typographies都建模为map-of ::sm/uuid - schema的映射。同一文件中的schema:mediafile.cljc则描述了媒体对象字段name、width、height、mtype、media-id、thumbnail-id、is-local等对应文档中 File 携带的媒体资源。列表类型的文件则单独放置在同目录下pages_list.cljc、components_list.cljc、typographies_list.cljc 等——它们在文件命名上就直接体现了列表作为一种一等实体的建模思想。页面与组件Container 抽象实体关系第三张 UML 图引入了一个精妙的抽象Container是Page与Component的共同父类Container |- Page、Container |- ComponentContainer组合多个Shapeobjects(Container, Shape)建模为ShapeTreeShape通过 parent 指针自关联形成层级树Shape -- Shape : parent。设计动机Page页面与Component组件内部都包含一棵 shape 树并共享大量函数与逻辑。为避免重复Penpot 引入Container实体作为抽象层凡是既能用于页面又能用于组件的代码就统一操作 Container。这在源码层的印证非常直观文件类型工具 file.cljc 中把页面与组件归一化为容器序列(defn containers-seq [file-data] (concat (map #(ctn/make-container % :page) (ctpl/pages-seq file-data)) (map #(ctn/make-container % :component) (ctkl/components-seq file-data))))以及上面展示的update-container、get-container等同名逻辑正是文档用统一抽象覆盖二者的落地。类型目录下存在独立的 container.cljc、shape_tree.cljc前者提供make-container工厂同时支持:page与:component两种 kind后者封装整棵 shape 树的遍历与操作。ShapeTree 的构成规则ShapeTree表示一组在层级上相互关联的 shapes顶部的 frame 包含顶层 shapesframe 与其他 shapeframe 与 group 内部可以包含任何非 frame的 shape。换言之树形结构的父节点只能是 frame 或 group普通 shape 不能直接作为其它 shape 的父容器。Shapes数据模型中最重要的一等公民实体关系第四张 UML 图聚焦于单个 shape 的组成。Shape与以下属性对象全部是聚合关系o--属性对象说明Selrect图中位置与包围盒bounding box含 x、y、width、height 等几何量Transform2D 变换矩阵用于旋转或拉伸 shapeConstraints当容器 shape 尺寸变化时 shape 的响应式变化规则Interactions在 viewer 中展示时的交互行为定义Fill填充颜色及其选项Stroke描边颜色及其选项Shadow阴影选项Blur模糊选项Font文本类型 shape 的字体现有选项Content文本类型 shape 的文本内容块Exports针对该 shape 定义的导出设置同时Shape通过parent自关联即每个 shape 持有对其父容器的引用以及全部子节点引用。Shape 的双重身份SVG 节点的超集文档强调Shape 是模型中最重要、最基本的实体它对应 Penpot 设计稿中的一个 图层并且对应一个经过 Penpot 特殊能力增强的 SVG 节点。这种形状即增强 SVG的设计带来了方向相反的两套渲染/解析管线渲染方向仓库中存在把 Shape 渲染为 SVG 标签的代码且渲染结果随使用环境不同而增减内容——在工作区中可编辑、在 viewer 中可交互、在 shape exporter 或 handoff交付中最小化、在文件导出时附带元数据。导入方向存在把任意 SVG 文件导入并反向转换为 shapes 的代码。如果该 SVG 是 Penpot 导出的则读取其内嵌元数据以精确还原形状如果不是 Penpot 导出的则以 best effort尽力而为方式推断属性。属性聚类与源码对应除了标识性属性id、name、type之外Shape 拥有大量属性。文档指出 Penpot 倾向于将它们组织成相关聚类cluster这正是面向对象化建模而非扁平字段化建模的原因。这些属性类在源码层有各自独立的类型定义文件shape.cljc核心 Shape schema 本体把多组属性 schema 以 merge 的方式组合如schema:shape-base-attrs、schema:shape-generic-attrs、schema:shape-geom-attrs的叠加并针对 rect/group/image/text 等不同类型使用不同 attr 组合结构上即体现了按聚类组织属性、按类型差异化的思想shape/attrs.cljc、shape/radius.cljc等对应几何与样式细节变换矩阵相关modifiers.cljc布局与约束shape/layout.cljc交互shape/interactions.cljc填充/描边/阴影/模糊fills.cljc、stroke.cljc、shape/shadow.cljc、shape/blur.cljc 与shape/background_blur.cljc文本字体与内容font.cljc、shape/text.cljc、text.cljc导出shape/export.cljc。需要说明的是各源码文件中的 schema 会随版本演进而与文档中的概念分组存在细微差异例如 blur 已区分为普通 blur 与 background blur阅读时可相互参照。形状与库的联动文档虽未展开但结合仓库可补充一个与组件体系直接相关的点shape 通过组件引用与库发生关联。例如 file.cljc 中get-component类函数先解析 shape 上的component-id/ 所属库再深入(dm/get-in libraries [library-id :data])找到库容器里的组件定义。这说明主文件中的 shape 可以借库文件中的组件定义来渲染正是文档 File 关系图中libraries自关联的实际用途之一。从概念到落地一条数据链路小结综合原文档与仓库源码可以梳理出 Penpot 数据的宏观链路组织层Profile用户⇄Team团队→Project项目→File设计文件由共享类型 profile.cljc、team.cljc、project.cljc、file.cljc 定义概念由 SQL 迁移0002、0003、0028、0063等在 PostgreSQL 中建表落地。文件内容层File的data属性携带pages向量与pages-index索引以及components、colors、typographies、媒体与 tokens 等资产集合。容器层Page与Component统一抽象为Container见 container.cljc各自持有一棵 shape 树。元素层树中的每个节点都是Shape——一个属性按几何、变换、约束、交互、样式、文本、导出等聚类组织、以 parent/children 关联的增强型 SVG 节点见 shape.cljc。由于概念在各端的表现形式略有差异同一份设计意图在前端表现为可编辑的响应式对象、在后端 RPC 中表现为校验过的消息体、在数据库中被拆分为 file / file-data / storage-object / comment-thread / share-link 等多张关联表。理解这一概念模型层是深入 Penpot 后端 RPC 服务、文件导入导出管线与前端 workspace 渲染架构的共同起点。后续若需深入某一部分可继续阅读技术指南中对应的数据访问、RPC 与存储章节并在 common/src/app/common/types/ 中逐一对照各实体的真实 schema 定义。【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表