返回博客列表
系统设计2026年8月20日2 分钟阅读
多租户 SaaS 收银系统:如何划分服务与数据边界
基于真实收银系统工程,讨论租户、门店、商品、库存、交易和支付服务如何拆分,以及哪些边界不能只靠约定。
#Go#MySQL#Redis#go-zero#多租户

收银系统看起来像一组商品与订单页面,真正复杂的部分却是数据归属和状态边界。同一个用户可能管理多个门店,同一商品可能拥有多个 SKU,库存变化又必须能追溯到交易、退货或人工调整。
先划分数据所有权
auth:用户、Token、角色与权限
merchant:租户、门店、员工和终端
product:商品、SKU、条码和价格
inventory:仓库、可用量、冻结量和库存流水
trade:订单、退货与班次
payment:支付、退款、通知与对账
租户上下文不能来自请求体
客户端只提交业务参数。网关从 JWT 解析用户身份,再根据授权关系得到 tenantId 与 storeId。下游服务从可信上下文读取归属字段,Repository 的查询条件也必须包含租户约束。
WHERE tenant_id = ? AND store_id = ? AND id = ?为什么库存独立
订单创建不等于库存已经扣减。库存服务需要处理预占、释放、确认扣减和人工调整,并生成不可随意修改的流水。如果库存只是商品表上的一个数字,退货和并发支付很快会让数据无法解释。
同步与异步的界线
用户等待结果的核心路径尽量同步返回明确状态;支付通知、库存流水和对账等需要重试与追溯的步骤通过事件解耦。Outbox 让业务写入与待发送事件处在同一个数据库事务。
服务拆分的代价
本地开发需要同时管理多个进程和依赖
跨服务查询必须重新设计,不能依赖随意 JOIN
错误码、身份上下文和幂等策略必须统一
运维必须补齐健康检查、日志、指标和契约生成
微服务不是目录结构,而是数据所有权和失败边界。
当前选择
项目保留 8 个清晰的业务服务,但不继续为了数量拆分。能够在一个服务事务内完成的流程保持内聚,只有具备独立数据和失败模式的领域才成为服务。