单体和微服务描述的是系统如何划分与部署。它们没有绝对优劣;关键是边界是否清晰,以及额外复杂度是否值得。

单体应用

单体应用通常把界面后端、业务逻辑和数据访问放在一个可部署程序中。它的优点是本地开发、事务和部署相对直接;随着系统扩大,不同模块可能互相依赖,发布也需要整体进行。 “单体”不等于“代码混乱”。一个设计良好的单体仍可有严格的内部模块边界。

微服务

微服务把系统拆成多个独立进程,通过 HTTP 等方式通信。每个服务围绕一组职责,可以独立运行和演进。 它带来的代价包括网络失败、跨服务调试、配置同步、接口兼容和数据一致性。把代码拆成多个服务器并不会自动获得良好架构。

服务边界和数据所有权

一个可靠的边界应明确:
  • 服务负责哪些业务能力;
  • 它拥有哪些实体和持久化数据;
  • 调用方只能通过哪些接口访问;
  • 哪些职责明确不属于它。
在 RakuLLApp 中,Controller 没有数据库,Model 服务各自拥有状态。这可避免两个服务同时把自己当作同一份数据的权威来源。

编排与调用链

编排(orchestration)是由一个协调者决定调用顺序。例如上传文章时,Controller 依次调用 Agent 计算、Model 保存,并向客户端推送进度。
客户端 -> Controller -> Agent
                    -> Model
调用链越长,越需要处理超时、部分失败和重试。无状态服务可以重启,但内存中的任务状态可能丢失;持久业务状态必须交给 Model。

有界上下文

有界上下文(bounded context)是领域驱动设计中的概念:某组业务词汇和规则只在一个明确边界内成立。例如“收藏夹和卡片”由 collection_server 管理,它不应顺便成为文章或用户的所有者。 边界之间通过 ID 和 API 契约合作,而不是直接读写对方数据库。

RakuLLApp 为什么拆分

RakuLLApp 把可替换的 AI 计算、请求编排和长期数据分开。目标不是追求服务数量,而是让数据所有权明确,并允许 Agent、存储和客户端分别替换或演进。
判断代码放在哪里时,先问“谁拥有这份状态”和“这条规则属于哪个实体”,再问“哪个服务调用最方便”。
具体服务与调用方向见 架构总览