在领域驱动设计(DDD)中,接口层主要负责处理与外部系统的交互,包括接收用户或外部系统的请求,调用应用层服务处理请求,以及将处理结果返回给请求方。

我发现一些代码中,接口的返回值类型众多,有的直接返回数据传输对象(DTO),甚至直接返回数据对象(DO),还有的返回Result对象。在DailyMart项目中,为了简化客户端的处理流程,我们决定在接口层采用统一的返回格式——Result对象。

1. 统一返回格式

1.1 构建Result对象

为了实现统一返回格式,我们在DailyMart项目中构建了一个Result对象,代码如下:

@Data
@Accessors(chain = true)
public class Result<T> {
    public static final String SUCCESS_CODE = "OK";
    private String code;
    private String message;
    private T data;
    private long timestamp;
}

为了便于创建Result对象,我们构建了一个辅助类ResultHelper:

@Slf4j
public class ResultHelper {

    public static <T> Result<T> success(T data) {
        return new Result<T>()
                .setCode(SUCCESS_CODE)
                .setData(data)
                .setTimestamp(System.currentTimeMillis());

    }

    public static <T> Result<T> fail(String message) {
        return new Result<T>()
                .setCode(ErrorCode.SERVICE_ERROR.getCode())
                .setMessage(message)
                .setTimestamp(System.currentTimeMillis());

    }
...
}

1.2 优化DailyMart中的接口

以DailyMart系统的注册接口为例,定义了Result对象后,我们可以在接口层这样优化代码:

@PostMapping("/api/customer/register")
public Result<UserRegistrationDTO> register(@RequestBody @Valid UserRegistrationDTO customerDTO){
 try {
  return ResultHelper.success(customerService.register(customerDTO));
 }catch (Exception e){
  return ResultHelper.fail(e.getMessage());
 }
}

为了避免每个接口都这样写,我们可以利用SpringBoot的全局异常处理器来处理,这将在下一节讨论。

1.3 优化后的结果

现在,当访问注册接口时,成功会返回如下响应:

{
    "code": "OK",
    "message": null,
    "data": {
        "userName": "jianzh1",
        "password": null,
        "email": "jianzh5@162.com",
        "phone": "18811117882"
    },
    "timestamp": 1687338445851
}

失败时会返回如下响应:

{
    "code": "B0001",
    "message": "用户已存在",
    "data": null,
    "timestamp": 1687338319457
}

这样,我们成功地实现了接口层的返回格式的统一。

2. 异常控制

在DailyMart的代码实现中,我们通常会在遇到问题时抛出RuntimeException。例如,在用户登录时,如果用户不存在,我们会抛出一个RuntimeException: