返回项目列表
DB Designer 标志

DB Designer

用于创建、可视化和管理实体关系图(ERD)的工具。

+1k 日访问量ReactTailwindPWAIndexedDB

在本文档中,我将详细介绍在 DB Designer 中实现的架构分析、设计模式和技术决策。DB Designer 是一款面向客户端的工具,旨在创建、可视化和管理实体关系图 (ERD)。

业务实用性与通用工作流

我构建 DB Designer 的主要目的是解决快速、直观地对数据库模式进行建模的需求,且无需依赖云端基础设施。我经常观察到,数据库设计工具需要复杂的配置、用户账户或与真实数据库的连接。我决定通过将系统设计为完全离线运行并通过 IndexedDB 在本地持久化信息来消除这一障碍。

我构建的核心工作流非常直观:

  1. 初始化:用户访问主仪表板 (Dashboard),可以在其中查看以前的项目或初始化一个新项目(从头开始或使用预定义模板)。
  2. 视觉建模:在我编写的编辑器中,用户添加代表表单的节点。我在每个表单中实现了动态字段添加,允许指定其名称、数据类型以及是否充当主键 (PK) 或外键 (FK)。
  3. 交互式关系:在连接节点时,我的系统通过图形连接推断语义,自动生成关系的视觉表示(例如 1:N)。我开发了一个智能路由系统 (Smart Edges),以在视觉上优化连接点的位置。
  4. 可视化与导出:用户可以预览基于视觉模型生成的 SQL 代码,并将整个图表导出为图像格式。我添加了此功能,以确保设计可以轻松集成到外部文档中。

深入架构分析与模型设计

我将该项目构思为面向客户端的架构(Client-Side Architecture),专注于浏览器的自主性,利用 Next.js 的 App Router 功能,但将计算和存储负担完全转移到客户端。

整体架构

我精心设计的应用设计反映了保持代码解耦、可维护和高效的关键模式:

  • Local-First Architecture:我通过 idb 库使用 IndexedDB 将数据持久化完全委托给用户的浏览器。我集成这一点是为了确保按设计保护隐私、低延迟和离线可用性(通过 PWA 增强)。我特意避免耦合后端来管理中央状态。
  • 可预测的状态管理:我将 Zustand 用于全局状态(如节点、边、撤销/重做历史记录),以防止 prop drilling 并促进交互式画布 (React Flow) 所需的响应式更新。
  • 职责分离:我将视觉组件 (UI) 隔离在可重用的组件(按钮、选择器、弹出框)中。我将图表的数学逻辑(通过 dagre 进行自动布局定位和智能边计算)提取到位于 /app/lib/ 中的纯模块中。
graph TD
    UI[React/Tailwind 用户界面] --> RF[React Flow 画布]
    RF --> |变更事件| Zustand[Zustand 状态管理器]
    Zustand --> |订阅/保存| IDB[(本地 IndexedDB)]
    UI --> |导出| H2I[html-to-image]
    UI --> |SQL 预览| Monaco[Monaco Editor]

    subgraph "业务逻辑 (lib)"
        AutoLayout[Dagre 自动布局]
        SmartEdges[连接计算]
    end
    Zustand <--> AutoLayout
    RF <--> SmartEdges

数据建模与逻辑

我将领域模型的定义集中在严格的 TypeScript 类型 (/app/types.ts) 中,这使我能够保证整个数据流的一致性。我围绕 ER 图的结构组织了主要实体:

  • Project:包含图表元数据(ID、名称、创建和更新日期)以及节点和边集合的根实体。
  • TableNodeData:每个 React Flow 节点的核心载荷。包含表标签、其语义颜色和列数组。
  • Column:定义表的属性,包括其 SQL 数据类型、标识符以及主键或外键约束的布尔标志。
classDiagram
    class Project {
        +String id
        +String name
        +Number createdAt
        +Number updatedAt
        +AppNode[] nodes
        +AppEdge[] edges
        +EdgeSettings edgeSettings
    }

    class AppNode {
        +String id
        +String type
        +Object position
        +TableNodeData data
    }

    class TableNodeData {
        +String label
        +String color
        +Column[] columns
    }

    class Column {
        +String id
        +String name
        +ColumnType type
        +Boolean isPk
        +Boolean isFk
    }

    Project "1" *-- "*" AppNode : 包含
    AppNode "1" *-- "1" TableNodeData : 载荷
    TableNodeData "1" *-- "*" Column : 定义

我以不可变的方式管理节点状态。界面上的交互式事件(如按“Enter”添加字段)会派发粒度化的动作,我对其进行了编程,使其仅更新 Zustand store 中的相关片段。

技术栈

我做出的技术决策反映了我对高级交互性和开发者体验的关注:

工具在架构中的技术角色
Next.js 16.1 (App Router)我选择它作为页面路由、项目结构和 PWA 配置的基础框架。
React 19主要 UI 库;我用它来管理响应性、Portal 组件和 DOM 事件。
Zustand我将其用于异步和同步全局状态管理。它支持我实现的编辑器撤销/重做系统。
React Flow (@xyflow/react)我将其集成作为视觉图表编辑器的核心渲染引擎。它允许我管理交互式画布、节点和连接。
IndexedDB (idb)它为我提供了浏览器中的异步本地存储。支持我针对 Project 模型做出的 Local-First 持久化决策。
Tailwind CSS v4这是我基于实用程序的样式系统;我用它来管理应用于亮/暗模式的语义 CSS 变量。
Monaco Editor我集成的代码编辑器组件,用于提供 SQL 语法的强壮预览。
Dagre我将其用作有向图的自动布局引擎,在视觉上组织复杂的图表模式。

设计决策的影响

我设计的架构在界面发生大量快速变更(拖动节点、编辑交互文本)的环境中最大化了响应速度。通过应用本地异步持久化和轻量级状态管理器,我成功完全避免了网络瓶颈。我对 TypeScript 严格类型的详尽使用以及层分离(将呈现与视觉几何计算隔离)确保了项目保持高度稳定性。这种结构使我能够在未来扩展系统,添加数据库模式中的新分析或生成功能,而无需进行破坏性的重构。

拖动可移动图示。按 + 和 − 缩放,方向键移动,0 重置视图。

一起合作

软件工程师、高级全栈开发工程师,常驻玻利维亚Santa Cruz,远程办公。欢迎新的工作机会。