ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Java企业多模型接入困局破解:适配器模式与模型路由实战

Java企业多模型接入困局破解:适配器模式与模型路由实战

最近一年,AI大模型API的价格和接入门槛被卷到了历史低点,很多Java团队都开始把手头的存量业务往AI上靠。但真上手之后,绝大多数团队遭遇的第一个意外,不是模型效果不合格,而是接入层先乱套了:不同厂商给的SDK风格完全不一样,请求参数命名千奇百怪,返回的JSON字段各玩各的,流式输出有的走SSE、有的走WebSocket,还有的只支持同步轮询。业务代码被迫给每一家模型单独维护一套对接逻辑,换模型等于换一套实现。这个味道太熟悉了,跟前些年企业对接各种支付通道简直一模一样。很多Java企业做AI应用开发,瓶颈根本不在提示词工程,而是卡在了多模型接入这件事上。

这篇文章我会从实战角度拆解:为什么Java企业接入多模型这么拧巴,核心矛盾到底在哪,以及怎么用一套轻量的适配层把问题解掉。会给出可直接抄作业的Spring Boot实现方案,也会把踩过的坑和排查思路一并交代清楚,适合正在做技术选型或已经在为多模型接入头疼的Java工程师、架构师参考。

1. 困局拆解:Java企业接入大模型,痛点到底在哪

1.1 大模型的世界是Python的,但企业应用是Java的

先说一个很多Java团队入坑后才意识到的现实:大模型生态的工具链几乎全部以Python为第一优先。模型训练、微调、推理框架、量化工具、部署脚本,甚至各家模型服务商最完整的SDK,都是先有Python版本,再考虑其他语言。这不单是历史惯性,也是技术栈决定的——深度学习生态的底层库、算子实现、推理引擎大多用C/C++和CUDA编写,Python只是那层最薄的胶水。

但这跟Java企业有什么关系?关系太大了。国内绝大多数企业的核心业务系统,尤其是交易、订单、库存、用户、权限这类关键链路,跑的还是Java那一套:Spring Boot、Spring Cloud、关系型数据库、消息队列。这些系统不会因为AI浪潮就推倒重写,大型企业更不可能为了接一个模型API把核心业务栈换掉。于是矛盾就出现了:模型世界默认你从Python来,企业现实要求你在Java里落。

这个矛盾在“能不能调通API”这个层面其实不难解决,因为模型服务商基本都提供HTTP接口,Java用任何HTTP客户端都能调。真正的难点是“规模化接入”:当你需要同时接多家模型、需要做容灾切换、需要统计成本、需要统一日志和权限管控时,纯靠业务代码各自为战地调用,很快就会被不断膨胀的适配代码淹没。

举个例子,某个电商平台的客服工单分类模块,最开始只接了一个通用对话模型,业务代码直接调用该模型的SDK,很顺畅。后来发现这家模型在处理售后纠纷类工单时效果不够好,又引入了另一家更擅长中文语义理解的模型。这时候问题来了:新模型的请求结构、超时设置、错误码语义、返回字段全都不一样,原来的业务代码根本没法复用。更麻烦的是,运营团队要求“哪家便宜且效果达标,就把流量切过去”,这个诉求直接逼着技术团队做一个中间层,否则每次模型切换都是一轮伤筋动骨的改动。

1.2 多模型接入的三个真实矛盾:协议、格式、业务集成

我把Java团队接多模型时遇到的麻烦归纳成三个层面,基本每个团队都会踩到:

第一是协议层。现在主流模型服务几乎都开始兼容同一套对话接口规范,算是行业默认的“普通话”。但兼容不等于完全一致,不同服务商在超时行为、鉴权方式、限流返回码、可选参数上仍然各有脾气。有的把API Key放在Header里,有的要求写进Authorization Bearer,有的还非要额外的组织ID字段。这些差异看起来不起眼,但落到代码里就是一堆if-else。

第二是数据格式层。表面上各家都传JSON,但字段命名、嵌套结构、流式事件格式差异很大。有的模型返回choices[0].message.content,有的返回output.choices[0].text;有的流式事件每帧含完整增量,有的必须自己拼delta字段;更别提部分模型会返回额外字段,比如usage里的prompt_tokens_details、logprobs等,处理不好反序列化直接报错。

第三是业务集成层。企业接入AI不是“调一个对话接口”就完事,而是要把模型嵌进具体业务流程里。比如工单系统的自动分类,需要把工单标题、描述、历史处理记录组装成提示词,再解析模型返回的分类结果,写回业务表。这要求接入层必须跟业务系统的数据结构、权限模型、日志规范打通。这一层往往比调API本身更花时间,如果底层适配又乱,整个集成周期会被拉得非常长。

这三个矛盾叠加起来,就形成了标题里说的“多模型接入困局”:不是接不上,而是接上了之后没法持续、可控、低成本地维护。

1.3 为什么不能每个业务模块自己对接模型API

有些团队会觉得,反正模型就两三家,各自封装一套调用工具类不就行了?我见过这么干的团队,半年后代码库里的模型调用逻辑散落在十几个业务模块里,每家模型一套封装,既没有统一超时策略,也没有统一的失败处理。代价会在几个时刻集中爆发:

第一个爆发点是密钥管理。开发环境、测试环境、生产环境各自的API Key散落在代码和配置文件里,某天安全扫描发现一个密钥被提交到了代码仓库,全组被迫轮换所有Key,排查范围极其痛苦。

第二个爆发点是故障时没有统一降级方案。某个模型服务商突然限流或者服务不稳定,业务模块只能各自处理。运气好的模块有重试逻辑,运气差的模块直接超时堆积,甚至拖垮整个线程池。

第三个爆发点是“切换成本”。业务侧想从贵的模型切到便宜的模型,本以为改个配置就行,结果需要修改所有调用方的代码,排期一周。当你的接入层没有抽象,任何切换都意味着回归测试和一排CR。

所以我的观点很明确:Java企业做AI应用开发,第一件该做的事情不是在提示词上抠细节,而是先把模型接入层设计好。接入多个模型这件事本身是技术债的重灾区,谁先把它抽象掉,谁后面就轻松。

2. 破解核心思路:先抽象再接入,把“模型网关”建在代码里

2.1 统一交互层要抽象到什么程度

破解多模型接入困局,核心思路一句话:业务代码不直接依赖任何一家模型的具体实现,只依赖一套自家中立的模型交互接口。这句话听着像老生常谈,但真正落地时最常见的争议是“抽象到什么粒度”以及“会不会过度设计”。

我的经验是,抽象层只需要覆盖你最常用的几个能力,不要一上来就想着把所有模型特性都统一了。对于绝大多数企业场景,抽象四个能力就够:

  • 基础对话(同步返回完整结果)
  • 流式对话(增量返回,用于打字机效果)
  • 模型列表查询(用于管理台展示和运维排查)
  • Token用量统计(用于成本核算和配额控制)

至于函数调用、多模态输入、嵌入向量这类高阶能力,可以放在扩展接口里,不要让它们污染主接口设计。抽象层越薄越好,它的职责边界就是“让业务方只关心我要发什么消息、拿什么回复”,至于消息是怎么路由到哪家模型、超时重试怎么处理、返回后怎么转译,都是适配层内部的事。

这里我经常用的一个类比是“转接头”。你出差带一个Type-C转接头,不管酒店电视是HDMI还是VGA,都能接上。模型适配层就是这个转接头,业务方永远只对着Type-C这头说话,底下换什么线不关他们的事。

不过有一点要提醒:抽象层不应该试图抹平模型能力的差异。比如A模型支持40K上下文,B模型只支持8K,这种差异你瞒不住业务方。好的做法是在模型元信息里暴露出来,让路由层和业务侧都能感知,而不是硬把接口设计成“所有模型都能处理任意长文本”。

2.2 适配器模式与模型路由:让换模型像切数据源一样简单

具体到Java实现,最顺手的套路就是经典的适配器模式加策略模式组合。每个模型供应商实现同一个ChatClient接口,接口内部完成自家协议的转换,然后通过一个路由组件去决定当前请求走哪一个实现。

这中间比较容易踩的坑是路由策略设计。很多团队一上来就做“随机路由”或者“纯轮询”,真实业务里这不够。我建议至少支持三种路由模式:

  • 加权轮询:根据权重分配流量,适合两三个模型水平接近、希望按比例分担的场景。
  • 故障转移:主模型调用失败或者超时后自动切换备用模型,适合对可用性要求高的核心链路。
  • 固定路由:按业务线固定走某个模型,适合某些模块因为合规或效果原因只能绑定特定模型的情况。

路由层保存在内存里就行,不需要上独立服务。真正复杂的企业可能会把模型网关拆成一个独立中间件,但对大多数团队来说,以SDK形式嵌在调用方里、通过配置中心动态调整路由比例,性价比最高。等接入的模型数量到了两位数、接入方也多了,再考虑把网关抽成独立服务也不迟。

2.3 配置、降级、可观测性:企业落地的三项硬指标

除了接口抽象和路由,企业级多模型接入还有三件容易被忽略但很重要的事:

配置中心化是刚需。模型服务的Base URL、API Key、模型名、超时阈值、并发上限,这些信息绝不能散落在业务代码里。统一收口到配置中心之后,运维改任何一项都不需要动代码发版。尤其API Key一定要走环境变量或专门的密钥管理服务,不能写死在yaml里提交到仓库。

降级策略必须提前定。多模型接入最大的好处就是“鸡蛋不放在一个篮子里”,但这个好处只有在你提前实现了降级逻辑时才成立。主模型整体不可用时,要能自动切换到本地部署的开源模型或者备用云厂商。降级不是简单的try-catch吞异常,而是要区分错误类型:限流错误要做退避重试,鉴权错误要立即报警,上下文超长要提示业务方缩减输入。一刀切的重试是很多系统雪崩的起点。

可观测性决定了你晚上能不能睡好觉。每次模型调用都要记录:走的哪个模型、耗时多少、Token消耗多少、返回码是什么、是否走了重试或降级。这些指标不光是运维需要,更是技术团队向业务方证明“哪家模型性价比更好”的关键证据。没有数据支撑的模型选型,最后都会变成拍脑袋。

提示:如果你所在团队还没有统一的日志规范,至少保证模型调用日志单独打印到一个独立Logger里,方便后续接入指标采集系统。

3. 实操落地:手写一个极简多模型接入层(Spring Boot 3 + Java 17)

3.1 统一接口与数据结构定义

理论讲多了容易飘,直接上能跑的代码。下面这套实现我建议用Spring Boot 3.2以上版本和Java 17,因为Spring Boot 3.2引入了RestClient,写同步HTTP客户端比RestTemplate干净很多,也比WebClient少一层响应式的心智负担。对于绝大多数Java团队,WebFlux那套响应式栈不是必须的。

先定义统一的数据结构。用Java的record很合适,简洁且不可变:

public record ChatMessage(String role, String content) {} public record ChatRequest( String model, List<ChatMessage> messages, double temperature, int maxTokens ) {} public record ChatResponse( String content, String finishReason, TokenUsage usage ) {} public record TokenUsage(long promptTokens, long completionTokens) {}

然后是统一的客户端接口。我刻意把流式处理做成同步回调模式,这样在传统Java服务里最容易理解和维护:

public interface ChatClient { ChatResponse chat(ChatRequest request); void chatStream(ChatRequest request, Consumer<String> onDelta); }

这个接口就是整个接入层的“宪法”。业务方只依赖它,永远不需要知道背后是哪个模型、哪个厂商。以后不管接多少家,业务代码一行都不用改。

3.2 用适配器实现“OpenAI兼容协议”接入

搭好了接口,接下来实现一个通用的适配器。因为现在绝大多数模型服务商都提供OpenAI兼容协议,先把这个通用适配器写出来,等于覆盖了市面上大半模型。

核心逻辑分三块:构造请求体、发起调用、解析响应。请求体构造是把统一的ChatRequest转换成OpenAI兼容格式,这一步各家差异主要在这里:

@Component public class OpenAiCompatibleChatClient implements ChatClient { private final RestClient restClient; public OpenAiCompatibleChatClient(@Value("${ai.model.base-url}") String baseUrl, @Value("${ai.model.api-key}") String apiKey) { this.restClient = RestClient.builder() .baseUrl(baseUrl) .defaultHeader("Authorization", "Bearer " + apiKey) .defaultHeader("Content-Type", "application/json") .build(); } @Override public ChatResponse chat(ChatRequest request) { Map<String, Object> body = new HashMap<>(); body.put("model", request.model()); body.put("messages", request.messages().stream() .map(m -> Map.of("role", m.role(), "content", m.content())) .toList()); body.put("temperature", request.temperature()); body.put("max_tokens", request.maxTokens()); Map<String, Object> response = restClient.post() .uri("/chat/completions") .body(body) .retrieve() .body(new ParameterizedTypeReference<Map<String, Object>>() {}); return parseResponse(response); } private ChatResponse parseResponse(Map<String, Object> response) { var choices = (List<Map<String, Object>>) response.get("choices"); var first = choices.get(0); var message = (Map<String, Object>) first.get("message"); var usage = (Map<String, Object>) response.get("usage"); return new ChatResponse( (String) message.get("content"), (String) first.get("finish_reason"), new TokenUsage( ((Number) usage.get("prompt_tokens")).longValue(), ((Number) usage.get("completion_tokens")).longValue() ) ); } }

这段代码里我对响应解析做了简化假设:按标准字段名取choices、message、content。实际开发中强烈建议用Jackson的JsonNode配合一个宽松的反序列化器,因为部分模型的字段名会带前后缀,或者有的模型顺手把message改成了output。解析层多几分容错,后面能少很多线上问题。

3.3 加权轮询路由与故障转移:当主模型挂了怎么办

单适配器只能接一家,要接多模型就得把路由层加上。路由层有两个职责:选出模型、处理故障。

我一般用一个简化的加权轮询实现。为什么选加权轮询而不是随机?因为业务侧经常希望“A模型承担80%流量,B模型承担20%”,加权轮询直观且稳定,配合配置中心可以随时调整权重。实现上用AtomicInteger做递增计数就能避免并发问题,不需要加锁:

@Component public class ModelRouter { private final List<ModelEndpoint> endpoints; private final AtomicInteger counter = new AtomicInteger(0); public ModelRouter(List<ModelEndpoint> endpoints) { this.endpoints = endpoints; } public ModelEndpoint next() { int totalWeight = endpoints.stream().mapToInt(ModelEndpoint::weight).sum(); int index = counter.getAndIncrement() % totalWeight; for (ModelEndpoint ep : endpoints) { index -= ep.weight(); if (index < 0) { return ep; } } return endpoints.get(0); } } public record ModelEndpoint(String name, int weight, ChatClient client) {}

这段路由的核心思路是把权重想象成一段一段的标尺,计数器落在一段上,就选择对应的模型。实现简单、线性时间复杂度,对常见的几个端点完全够用。

故障转移就建立在路由之上。做一个门面类,把“调一次”变成“调一轮”:

public ChatResponse chatWithFailover(ChatRequest request) { Set<String> attempted = new HashSet<>(); for (int i = 0; i < endpoints.size(); i++) { ModelEndpoint ep = router.next(); if (!attempted.add(ep.name())) { continue; } try { log.info("route to model: {}", ep.name()); return ep.client().chat(request); } catch (Exception e) { log.warn("model {} failed: {}", ep.name(), e.getMessage()); } } throw new RuntimeException("all models failed"); }

这里有个细节值得展开:重试前过滤掉已经失败的端点,避免死循环。还有,异常判断上要区分4xx和5xx。5xx肯定是服务端问题,值得重试;4xx比如鉴权失败、请求参数非法,重试一百遍结果也一样,直接抛出比闷头重试强。

3.4 完整配置与启动验证

配置层面,我希望多个模型的配置集中在一个地方,并且API Key通过环境变量注入。用一个配置类和yaml文件配合:

ai: models: - name: primary base-url: https://api.example-a.com/v1 api-key: ${MODEL_A_KEY} model: deepchat-pro weight: 5 timeout-seconds: 30 - name: backup base-url: http://internal-model-server:8000/v1 api-key: local-token model: local-llm-7b weight: 3 timeout-seconds: 60

配置类读取这段yaml,为每个模型创建一个ChatClient实例,再组装成带权重的Endpoint列表。这一步用Spring的@ConfigurationProperties可以很优雅地实现,代码不展开了,但有几个容易出错的地方提一下:

  • 每个模型要有自己的RestClient实例,不要复用同一个bean,否则不同模型的Base URL和API Key会串。
  • 超时时间务必为每个模型单独配置。本地部署的模型通常响应慢,用云厂商的30秒超时去调它,几乎必然超时。
  • 不同模型的SDK初始化最好放在同一个配置类里统一管理,方便排查“哪个模型没被正确装配”这类问题。

启动验证最简单的方式:写一个CommandLineRunner,启动后自动向两个模型各发一条“你好”,打印各自的响应和耗时。这一步能提前发现配置错误,而不是等接口被调用时才暴露。实测下来,这条经验至少帮我避过两次“本地能跑、线上全挂”的尴尬。

4. 工具选型:Spring AI、LangChain4j、自研适配层怎么选

4.1 三类主流方案的定位差异

聊完了自研方案,很多读者会问:现在不是有Spring AI、LangChain4j这些现成框架吗,为什么还要手写?这个问题问得好,我也没否认这些框架的价值,但选型之前要先看清它们的定位差异。

Spring AI是Spring官方推的AI应用开发框架,设计思路对标大家熟知的AI编排框架,抽象了ChatClient、EmbeddingModel等接口,支持多家主流模型服务商。它最大的优势是能无缝融入Spring生态,跟Spring Boot的配置体系、自动装配、Actuator监控结合在一起,对已有Spring体系的企业非常友好。我试用过几个版本,整体方向是对的,但目前版本迭代速度快,部分API还在调整,升级大版本时可能需要跟着改代码。

LangChain4j是社区驱动的Java版AI编排框架,功能覆盖面广,RAG、Agent、工具调用这些高级特性都有,跟Spring的集成也很成熟。如果团队要做的不是“接入API”而是“构建完整AI应用”,它确实能省不少事。代价是引入的概念和依赖比较多,学习曲线比自研适配层陡,团队对它的内部机制如果不熟悉,出了问题排查会有点吃力。

自研适配层就是我前面写的那种方案,只解决“多模型接入、路由、容灾”这件事,不做Agent、RAG这些编排能力。好处是代码量小、完全可控、没有框架升级包袱;坏处是后续如果业务需要完整的Agent链路,还得在这个地基上继续加东西。

4.2 选型决策清单:先问五个问题再动手

我给团队做技术建议时,一般先问五个问题,答案基本上能定方向:

第一,你的核心诉求是“接模型”还是“做AI应用”?如果只是把对话能力接到现有业务里,自研适配层或者Spring AI足够;如果要做知识库问答、多步骤任务的Agent应用,LangChain4j这类编排框架更合适。

第二,团队对框架升级的容忍度如何?愿意跟着官方版本节奏走、定期处理升级变更的,选Spring AI没问题;不允许在非核心路径上频繁折腾依赖的,自研更稳。

第三,你需要的模型能力是否主流?如果只用对话、流式对话、JSON输出这些基础能力,框架和自研都覆盖;如果你要用到某模型独有的高级特性,比如多模态输入的特殊参数,自研更容易做定制,因为框架的抽象层可能暴露不够充分。

第四,团队技术栈的心智负担能承受多少?传统Java团队如果对响应式编程不熟,尽量不要选需要写大量Flux/Mono代码的方案。学习成本也是成本,而且是很贵的成本。

第五,你的模型数量会增长到多少?两三家,自研最舒服;十家以上,强烈建议单独搭建模型网关服务,而不是继续用SDK嵌入模式。

4.3 我的建议与长期演进方向

把这几个问题过一遍之后,我给大多数存量Java企业的建议是:初期不要急着上重框架,用一个自制轻量适配层跑起来,把统一接口、路由、降级、监控这四个地基打扎实。等业务验证了AI场景的真实价值,再决定要不要引入更重的编排框架。

理由不复杂。存量系统的技术债已经不少,AI接入层的首要目标是不添乱。自研适配层代码不过几百行,核心就两个适配器和一张路由表,出问题几个小时就能摸透。而重框架一旦发生版本升级带来的不兼容,排查成本会成倍放大。

当然,自研适配层也不是终点。我见过比较理想的演进路径是:先用轻量适配层支撑两三个月业务验证,期间把调用日志和成本数据采集下来;确认业务量上涨后,再把适配层升级为独立网关服务,接入统一鉴权、多租户隔离、智能路由;最后在网关能力稳定后,再基于它去接RAG和Agent相关能力。这个路径每一步都建立在已有的资产上,不会推倒重来。

5. 高频坑与排查技巧:实战中那些文档里不写的问题

5.1 流式输出解析的坑

流式输出几乎是每个接入多模型团队都会翻车的地方。表面上各家都叫流式,细节却各有不同。最标准的是SSE格式,每行以data:开头,最后一行是data: [DONE]。但有的模型在流式过程中会间插空行,有的会在JSON前加注释,还有的会把usage信息放在流式事件最后一帧里。

我踩过最大的坑是按固定Buffer读流导致中文被截断。最初用InputStreamReader按char数组一次读1024个字符,结果模型返回的中文恰好被从中间切开,解析半截JSON直接报错。正确的做法是按行读取,SSE的data:字段本身是整行的,按行切不会碰到字符被拦腰斩断的问题。顺着这个思路,流式解析的核心逻辑其实就几行:

try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.getBody()))) { String line; while ((line = reader.readLine()) != null) { if (line.startsWith("data:")) { String data = line.substring(5).trim(); if ("[DONE]".equals(data)) break; onDelta.accept(parseDeltaContent(data)); } } }

如果你接的模型流式事件格式不是这种标准SSE,调试时一定先用curl把原始报文打出来看一遍,再对照着写解析逻辑。没有这个习惯,写出来的解析器大概率是“碰巧能用,一换模型就崩”。

5.2 超时与线程池耗尽

模型API是典型的外部依赖,对外部依赖最怕的不是慢,而是“慢到把线程池占满”。很多Java服务用Tomcat默认的200线程池接模型请求,某天模型服务商抖动,响应时间从2秒涨到30秒,200个线程全部卡在等待响应上,新请求排队,整个服务跟着雪崩。

解法是隔离和分级超时。每个模型单独配一个线程池或信号量,A模型慢只影响A模型的调用方,不拖累B模型。超时也要分层:连接超时、读取超时、总超时分开设。连接超时通常5秒足够,读取超时要根据模型最大Token数和生成速度估算。一个估算经验是:每1000个Token按30秒预算,再乘以你给业务承诺的响应时间系数。

注意:不要给模型调用设置“永远等待”。大模型生成时间跟输入长度、模型型号强相关,一个不设超时的调用,会在故障时刻变成宿主的定时炸弹。

5.3 模型切换引发的数据格式兼容

多模型接入后,最隐蔽的坑是“A模型返回的结果业务方能解析,换B模型后同一个解析器崩了”。原因通常是B模型在JSON里加了额外的结构,比如把回答内容包了一层带类型标记的对象,或者把多段内容放在数组里而不是字符串里。

针对这个问题,我建议在适配层统一出口,对外只暴露我们自定义的ChatResponse结构。适配器B解析回原始模型格式时,哪怕原始结构再花哨,也必须在适配器内部转成统一格式。这样,B模型格式的怪癖就被隔离在适配器内部,不会泄漏到业务层。另外,如果模型返回的JSON结构复杂,最好用JsonNode先做一次“字段不存在就用默认值”的兜底,而不是直接强转。

5.4 成本控制与降级策略

多模型接入后,成本核算从“一张账单”变成“多张账单、多种计费模式”,容易失控。有的模型按Token计费,有的按请求次数,还有的混合计费。没有统一标注,月底财务对账就是一场灾难。

我的做法是在路由层把每次调用的模型名、Token消耗、计费单价都打进日志,让运维平台按维度聚合。另外在路由里设置成本开关:当主模型当日的Token消耗逼近预算阈值时,自动把部分非核心流量降级到廉价模型。这个“廉价模型不一定效果差,只是响应稍慢”的事实,很多团队是在接入之后才发现的。

最后再分享一个容易忽略的细节:模型版本升级也会引发“隐性不兼容”。某家大模型的同一个接口路径,几个月后返回的字段可能会微调。因此,建议在适配层里加一个简单的响应结构校验,检测到关键字段缺失时打印警告日志,而不是让异常在业务代码里反复出现后才被人发现。这类问题早发现一天,就能少熬一个夜。

我个人在实际操作中的体会是,多模型接入这件事,真正的分水岭不在编码技巧,而在一开始是否愿意花半天时间把适配层骨架搭对。只要统一接口立住了,接第二家、第三家模型都只是“新增一个适配器”的体力活;如果偷懒跳过这一步,后续每一次模型调整都是在给系统埋雷。这个项目做完之后,我把路由和适配器的代码抽出来沉淀成了团队内部的公共模块,现在几个业务系统接入新模型,开发量基本压缩到了半天以内。你也可以从今天这个极简版本开始,慢慢长出适合自己团队的模型接入体系。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进