Moonshot AI API Java SDK | 记录一次DDD设计实践

1,155 阅读4分钟

最近有个项目要用到月之暗面Moonshot AI的API,翻了下官方文档发现没有Java版本的SDK,于是打算动手写一个Java SDK,同时也正好强化一下DDD实践。

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交互的业务流程,这里边只有两个接口:ApiServiceApiResponseService

  • ApiService负责处理向Api发送请求
  • ApiResponseService负责接收并处理Api的响应


infrasturcture层是基础设施,主要的目的是与第三方组件的衔接并起到一定的防腐作用,与核心业务代码进行一定程度的隔离,方便替换。
infrasturcture.client包里放的是与第三方组件的交互接口,俗称门面接口,这个项目中能用到的第三方组件主要有三个场景:log日志 http请求的客户端 json字符串的转换。比如log我们可以使用java.util.logging也可以使用sfl4jhttp我们可以使用Java 11 HttpClient也可以使用OkHttpClientjson我们可以使用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);
}

ApiGatewayClientLogClient我们分别用java.net.http.HttpClientjava.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 ObjectDTOModel的区别就是DTO通常不包含任何业务逻辑,它只是简单地封装了数据,非常轻量,易于传输。而Model除了包含属性及数据以外,还要封装与自身属性相关的业务逻辑,简单来说就是哪些业务逻辑会修改到Model自身的属性,这个Model里就要包含这些业务逻辑。
在本项目中dto里放的是api接口返回数据对应的Java类。

infrasturcture.exception包里是异常类。

infrasturcture.util包里是工具类。

moonshot-api-spring-boot

moonshot-api-spring-boot 这个工程实现了与springboot的衔接

  • client包里对JsonConverterLogClient这两个门面接口进行了实现,分别是Sf4jLogClient(用Spring中的Sfl4j来处理log)和JacksonJsonConverter(用Jackson来处理json)。
  • MoonshotApiAutoConfiguration.javaSpring的自动装填配置类,最终装填出一个MoonshotApiApplication,装填的条件是api-key这个值不能为空,否则就不进行装填。
  • MoonshotApiProperties.java是配置类,里边只有一个属性就是api-key,对应的配置文件路径是moonshot.api-key

结语

项目还有许多不完美的地方,之后我会慢慢优化:

  • 不支持多文件同时上传
  • 不支持异步通讯
  • 超大文件上传时的内存管理需要进行优化
  • 不支持stream方式(有接口但是没有进行实现)

源码我已经push到了Github上,感兴趣的同学可以自行查看,有任何问题欢迎留言或者issues。
具体的配置和使用手册我会写在Readme上。 接下来我会逐步进行测试,确保自测没问题后,我会发布到Maven上,有需要的同学可以关注一下。

在这个项目开始之前我看了很多关于DDD的文章,感觉自己似乎领悟到了,似乎又没有领悟。既然在工作当中无法实践,那么通过自己的一个小项目来进行消化是一个很不错的选择。

"一个人怎样才能认识自己呢?决不是通过思考,而是通过实践。" - 歌德