迁移基础 · 跨语言综合项目 · LESSON 25

模块化与跨语言架构

把业务规则、IO、设备和外部服务拆开,让代码可以单独测试、替换和跨语言协作。

16 分钟architecture · modularity · dependency inversion · interop

模块化与跨语言架构

AI 产品常用 Python 处理数据和模型,机器人运行时常用 C++,Web 或控制台又可能是 JS/TS。模块化的目标不是让三种语言写同样的代码,而是让职责通过稳定协议连接。好的架构先保护业务规则,再把 HTTP、文件、模型服务、设备和消息系统放到可替换的边缘。这样语言选择跟着性能、生态和部署约束变化,核心行为仍能被单元测试验证。

可以把系统想成四圈:领域规则、应用用例、外部适配器、入口与组装。Python 的模型调用、C++ 的硬件驱动和 JS 的 Web handler 都属于边缘;它们依赖一个清楚的输入输出接口,而不是让领域代码反向导入 SDK。跨语言时,用 JSON/HTTP、CLI 或消息协议传递数据与错误,不要试图共享语言内部对象。

学习目标

  • 能把一个业务用例拆成核心规则与外部适配器,并画出依赖方向。
  • 能为端口写出输入、输出和失败语义,而不把某种语言的 SDK 暴露给核心。
  • 能用 fake 验证核心流程,再说明替换为 Python 模型服务或 C++ 设备适配器时哪些代码不必改变。

把语言选择放在边界之后

下面这段代码只保留同一个意图,重点观察输入边界、数据流和失败语义,而不是逐字符翻译。

TRANSLATION LENS 同一个意图,两种工程表达 窄屏可左右滑动查看完整代码
JS / TS
export async function handle(request: Request) {
const data = await readInput(request);
return render(await runModel(data));
}
Python / C++
# Python
def run_pipeline(source, model):
  data = source.read()
  return model.predict(data)

// C++
Result Controller::handle(const Input& input) {
return model_->predict(input);
}

依赖反转与端口适配器

业务规则不应直接依赖 HTTP、文件或 ROS 2。先定义端口,例如 Model.predict(input) -> PredictionReadingStore.read() -> Reading,再由 Python/C++/JS 适配器实现。高层用例只依赖端口,入口负责组装具体实现;测试可以注入 fake。端口越小,跨语言协议越容易稳定,替换模型供应商或设备驱动的影响也越小。

class Model:
    def predict(self, features: list[float]) -> str:
        raise NotImplementedError

def classify(features: list[float], model: Model) -> str:
    label = model.predict(features)
    if not label:
        raise ValueError("model returned an empty label")
    return label

class FakeModel(Model):
    def predict(self, features: list[float]) -> str:
        return "ok" if sum(features) >= 0 else "reject"

按职责而不是按语言拆分

Web 层解析请求和认证,数据层做 schema/单位转换,领域层执行规则,模型适配器负责推理,设备适配器负责硬件,协议层定义跨进程消息。不要让一个 Python 服务直接知道 React 请求对象,也不要让 C++ 控制器解析任意 JSON 字符串。部署时可以把模块放在同一进程,也可以拆成服务;边界和契约先于进程数量。

常见错误与排错思路

常见错误是“共享数据库表就是共享接口”,导致三个语言组件互相依赖未声明字段;另一个是为了抽象而抽象,接口暴露了整个 SDK。排错时沿一条用例画依赖箭头,检查高层是否导入低层实现,观察单元测试是否必须启动网络或设备;若替换一个适配器需要改业务规则,说明依赖方向或数据模型仍未稳定。

用一个请求走通模块边界

先把用例写成依赖端口的普通函数。这里的 model 是适配器;测试时换成固定结果的 fake,业务函数仍然执行相同的校验和组装逻辑。

function classify(event, model) {
  if (!Number.isFinite(event.value)) {
    return { ok: false, error: "invalid_value" };
  }
  return { ok: true, id: event.id, label: model.predict(event.value) };
}

const fakeModel = { predict: (value) => value > 0.5 ? "alert" : "normal" };
console.log(classify({ id: "e1", value: 0.8 }, fakeModel));

这段代码刻意不关心模型文件在哪里、HTTP 如何连接或 C++ 设备如何读取数据。若核心函数开始读取环境变量、创建网络客户端或直接控制硬件,外部细节就越过了端口。

运行验证与替换实验

运行示例应得到 { ok: true, id: "e1", label: "alert" };再把 value 改成 NaN,结果应稳定变为 invalid_value,且 fake 不应被调用。下一步可把 fakeModel 替换为真实适配器,比较调用契约而不是重写业务规则。验收时记录依赖箭头、失败类型和一次替换前后的测试结果。

排查架构问题时,先问核心用例能否在没有网络、文件和设备的条件下运行。如果不能,追踪第一个直接创建外部资源的调用点,将它收进端口实现,并让测试注入 fake。

迁移练习

请完成:把一个“上传文件后调用模型再返回结果”的应用拆成 Web、数据、模型和协议四层,写出依赖方向、端口签名、一个 fake 和一个可观察性字段。

01
TRY IT YOURSELF

模块化与跨语言架构练习

把一个“上传文件后调用模型再返回结果”的应用拆成 Web、数据、模型和协议四层,写出依赖方向、端口签名、一个 fake 和一个可观察性字段。

给我一点提示

为每层写一句职责,再列出它依赖的外部世界;领域用例不要直接导入 Web 或 SDK。

查看参考答案
Web 解析请求并生成 request_id;数据层校验大小、格式和单位;用例依赖 ModelPort.predict(features);模型适配器连接 Python/远端服务;协议层定义 version、label、score 与错误码。FakeModel 返回固定结果,日志串联 request_id 和 model_version。
本节结论

按职责而不是按语言拆模块,才能把 JS/TS 经验真正带进 AI 与机器人系统。完成后,请用 fake 跑一次核心用例,再替换成真实模型或设备适配器,确认业务代码无需改动。

小结

架构迁移先稳定业务用例与数据契约,再为 HTTP、模型、文件和设备选择合适语言。下一步学习 API 契约时,把本课的端口签名补全为可校验的版本、超时和错误协议。

当前学习阶段跨语言综合项目
0/3

阶段共 3 节课,按顺序完成更容易建立完整的迁移模型。