Schema-First 方法与 ThingsDB

发布: (2025年12月12日 GMT+8 14:45)
5 min read
原文: Dev.to

Source: Dev.to

设置舞台:空白画布

我们首先为应用创建一个全新的集合。

new_collection('OrderApp');

理解类型化集合(核心防御)

保护的第一步是建立 schema(模式)。默认情况下,新的 ThingsDB 集合在根部只有一个空的、无限制的对象(一个基本的 thing)。任何拥有写权限的用户都可以向其中添加 任何东西

我们通过创建一个类型 App 来锁定它,使其成为集合整体存在的结构化蓝图。

// 1. 为我的集合根部创建一个类型
new_type('App');

// 2. 将集合根部转换为 'App' 类型
.to_type('App');

为什么重要: 现在集合受 App 类型限制。除非修改符合 App 类型定义的更改,否则无法添加或修改数据。这是你的主要模式防御。

定义数据模型

接下来,我们定义想要存储的数据结构——Order 对象——以及它在 App 根部的存放位置。

// 创建一个枚举器,以保持订单状态值的一致性
set_enum('OrderStatus', {
    OPEN: 'Open',
    PENDING: 'Pending',
    CLOSED: 'Closed',
});

// 定义 Order 结构
set_type('Order', {
    id: '#',               // 包装后返回的 ID 为 `id`
    name: 'str',
    price: 'float',
    status: 'OrderStatus', // 强制状态一致性
    created: 'datetime',
});

// 更新 App 类型:添加一个属性 'orders',其类型为 Order 对象数组
mod_type('App', 'add', 'orders', '[Order]');

mod_type(..) 调用会自动在集合根部创建一个空的 orders 列表,准备存放我们的数据。

建立事件通道

对于现代微服务或组件化应用来说,轮询数据变化效率低下。我们使用 Room 设置一个事件通道,以实现实时通信。

// 为 App 类型添加一个用于事件处理的 room 属性
mod_type('App', 'add', 'order_events', 'room');

// 为了便于识别,我们给房间一个固定名称
.order_events.set_name('api_order_events');

好处: 任何组件现在都可以加入此房间,立即对 new-orderchange-order-status 等事件作出响应,而无需不断查询集合。

用过程封装逻辑(API 层)

为了确保只能通过 Procedures 对集合进行有效更改,我们把所有操作 通过过程暴露。我们给这些过程加上 api_ 前缀,以便后续安全白名单的管理。

// 1. 添加新订单(对输入类型进行验证)
new_procedure('api_order_add', |name, price| {
    order = Order{
        name:,
        price:,
    };
    .orders.push(order);

    // 为所有监听者发送 'new-order' 事件
    .order_events.emit('new-order', order.wrap());
    nil;
});

// 2. 更改订单状态
new_procedure('api_order_set_status', |order_id, status| {
    // 输入验证:确保新状态是有效的枚举值
    new_status = OrderStatus{||status.upper()};

    // 访问并更新指定的 Order 对象
    Order(order_id).status = new_status;

    // 发送 'change-order-status' 事件
    .order_events.emit('change-order-status', order_id, new_status.value());
    nil;
});

// 3. 按状态检索订单
new_procedure('api_orders_by_status', |status| {
    order_status = OrderStatus{||status.upper()};
    .orders.filter(|order| order.status == order_status).map_wrap();
});

严格安全:受限的 API 用户

最后也是最关键的一步是创建一个只能 执行这些特定过程 的用户。这可以防止任何外部服务绕过你定义的业务逻辑。

// 为微服务访问创建新用户
new_user('api');

// 授予集合访问权限:
// CHANGE(修改数据),JOIN(监听事件),RUN(执行过程)
grant('//OrderApi', 'api', CHANGE|JOIN|RUN);

// 白名单:这是安全锁。
// 用户只能执行以 'api_' 开头的过程并加入以 'api_' 开头的房间
whitelist_add('api', 'procedures', /^api_.*/);
whitelist_add('api', 'rooms', /^api_.*/);

// 生成并返回认证令牌
new_token('api');

通过此设置,api 用户被完全限制。它不能随意删除集合、改变结构或在不经过你在 api_ 过程里定义的验证输入和逻辑的情况下修改数据。你的集合现在真正实现了代码层面的保护!

在下一篇文章中,我们将转向客户端,演示微服务如何使用生成的令牌连接到 OrderApp 集合,执行已认证的 api_ 查询,并通过 api_order_events 房间监听实时事件变化。

Back to Blog

相关文章

阅读更多 »