最近有个项目要用到月之暗面Moonshot AI的API,翻了下官方文档发现没有Java版本的SDK,于是打算动手写一个Java SDK,同时也正好强化一下DDD实践。
- 源码地址: moonshot-api-java | Github
- 官方文档: Moonshot AI
moonshot-api
核心的工程为moonshot-api,包结构如下:
com.iamuv.moonshot.api
├── application
├── domain
│ ├── model
│ ├── service
└── infrastructure
├── client
├── dto
├── exception
└── util
核心工程不依赖于任何第三方,这样设计可以减轻使用负担,但是同时也带来了一个问题:如何适配第三方组件,比如Jackson OkHttp Sfl4j,解决这个问题详见infrastructure层。
application层即整个应用的入口,相当于main方法。
domain层是领域驱动模型相关的,其中model包里是领域驱动模型类,大白话讲就是对应了Moonshot API里的几个功能分类,比如对话功能chat,文件功能files等等,API里有几种功能,我就创建了几个类,处理各自功能相关的业务流程,如下图:
com.iamuv.moonshot.api.domain.model
├── Api.java //这个是model层的超类
├── Chat.java
├── Files.java
├── Models.java
└── Tokenizers.java
model.service包里放的是核心的service,负责处理领域模型之间以及领域模型与第三方组件infrastructure交互的业务流程,这里边只有两个接口:ApiService和ApiResponseService
ApiService负责处理向Api发送请求ApiResponseService负责接收并处理Api的响应
infrasturcture层是基础设施,主要的目的是与第三方组件的衔接并起到一定的防腐作用,与核心业务代码进行一定程度的隔离,方便替换。
infrasturcture.client包里放的是与第三方组件的交互接口,俗称门面接口,这个项目中能用到的第三方组件主要有三个场景:log日志 http请求的客户端 json字符串的转换。比如log我们可以使用java.util.logging也可以使用sfl4j,http我们可以使用Java 11 HttpClient也可以使用OkHttpClient,json我们可以使用Jackson也可以使用Gson,于是client包里我们定义了三个接口:
ApiGatewayClient负责衔接Http请求的组件
public interface ApiGatewayClient {
ApiGatewayDto get(Map<String, String> headers, String url) throws IOException, InterruptedException;
ApiGatewayDto post(Map<String, String> headers, String url, String body) throws IOException, InterruptedException;
ApiGatewayDto file(Map<String, String> headers, String url, String content) throws IOException, InterruptedException;
ApiGatewayDto delete(Map<String, String> headers, String url) throws IOException, InterruptedException;
}
JsonConverter负责衔接Json组件
public interface JsonConverter {
String toJson(Object object);
<T> T fromJson(String json, Class<T> clazz);
<T> Collection<T> fromJsonToCollection(String json, Class<T> clazz);
<T> T fromJson(String json, String key, Class<T> clazz);
<T> Collection<T> fromJsonToCollection(String json, String key, Class<T> clazz);
}
LogClient负责衔接log组件
public interface LogClient {
void debug(String message);
void info(String message);
void warn(String message, Throwable e);
void error(String message, Throwable e);
void warn(String message);
void error(String message);
}
ApiGatewayClient和LogClient我们分别用java.net.http.HttpClient和java.util.logging.Logger进行了Default实现:
com.iamuv.moonshot.api.infrastructure.client
│ └── impl
│ ├── DefaultApiGatewayClient.java
│ └── DefaultLogClient.java
├── LogClient.java
├── JsonConverter.java
└── ApiGatewayClient.java
因为Java SDK中并没有Json相关的工具包,需要引入第三方库,但是正如前面所说的,这个核心项目的原则是不引入任何第三方库,所以没有给出默认实现。
infrasturcture.dto包里放的是Data Transfer Object。DTO与Model的区别就是DTO通常不包含任何业务逻辑,它只是简单地封装了数据,非常轻量,易于传输。而Model除了包含属性及数据以外,还要封装与自身属性相关的业务逻辑,简单来说就是哪些业务逻辑会修改到Model自身的属性,这个Model里就要包含这些业务逻辑。
在本项目中dto里放的是api接口返回数据对应的Java类。
infrasturcture.exception包里是异常类。
infrasturcture.util包里是工具类。
moonshot-api-spring-boot
moonshot-api-spring-boot 这个工程实现了与springboot的衔接
client包里对JsonConverter和LogClient这两个门面接口进行了实现,分别是Sf4jLogClient(用Spring中的Sfl4j来处理log)和JacksonJsonConverter(用Jackson来处理json)。MoonshotApiAutoConfiguration.java是Spring的自动装填配置类,最终装填出一个MoonshotApiApplication,装填的条件是api-key这个值不能为空,否则就不进行装填。MoonshotApiProperties.java是配置类,里边只有一个属性就是api-key,对应的配置文件路径是moonshot.api-key。
结语
项目还有许多不完美的地方,之后我会慢慢优化:
- 不支持多文件同时上传
- 不支持异步通讯
- 超大文件上传时的内存管理需要进行优化
- 不支持
stream方式(有接口但是没有进行实现)
源码我已经push到了Github上,感兴趣的同学可以自行查看,有任何问题欢迎留言或者issues。
具体的配置和使用手册我会写在Readme上。
接下来我会逐步进行测试,确保自测没问题后,我会发布到Maven上,有需要的同学可以关注一下。
在这个项目开始之前我看了很多关于DDD的文章,感觉自己似乎领悟到了,似乎又没有领悟。既然在工作当中无法实践,那么通过自己的一个小项目来进行消化是一个很不错的选择。
"一个人怎样才能认识自己呢?决不是通过思考,而是通过实践。" - 歌德