【DDD】Thoughtworks笔记(目录划分、异常设计)

参考:https://insights.thoughtworks.cn/backend-development-iteration0/  后端开发实践——开发者的第0个迭代

代码样例:https://github.com/e-commerce-sample/order-backend

目录设计:

1、首先基于业务分包(基于聚合根)

  早年的Java分包方式通常是基于技术的,比如与domain包平级的有controller包、service包和infrastructure包等。这种方式当前并不被行业所推崇,而是应该首先基于业务分包。比如,在订单示例项目中,有两个重要的领域对象OrderProduct(在DDD中称为聚合  根),所有的业务都围绕它们展开,因此分别创建order包和product包,再分别在包下创建与之相关的各个子包。此时的order包如下:

├── order
│   ├── OrderApplicationService.java
│   ├── OrderController.java
│   ├── OrderNotFoundException.java
│   ├── OrderRepository.java
│   ├── OrderService.java
│   └── model
│       ├── Order.java
│       ├── OrderFactory.java
│       ├── OrderId.java
│       ├── OrderItem.java
│       └── OrderStatus.java

更复杂场景:如果代码结构足够的简单,那么没有必要再次进行子包的划分,

├── order
│   ├── OrderApplicationService.java
│   ├── OrderController.java
│   ├── OrderPaymentProxy.java
│   ├── OrderPaymentService.java
│   ├── OrderRepository.java
│   ├── command
│   │   ├── ChangeAddressDetailCommand.java
│   │   ├── CreateOrderCommand.java
│   │   ├── OrderItemCommand.java
│   │   ├── PayOrderCommand.java
│   │   └── UpdateProductCountCommand.java
│   ├── exception
│   │   ├── OrderCannotBeModifiedException.java
│   │   ├── OrderNotFoundException.java
│   │   ├── PaidPriceNotSameWithOrderPriceException.java
│   │   └── ProductNotInOrderException.java
│   ├── model
│   │   ├── Order.java
│   │   ├── OrderFactory.java
│   │   ├── OrderId.java
│   │   ├── OrderIdGenerator.java
│   │   ├── OrderItem.java
│   │   └── OrderStatus.java
│   └── representation
│       ├── OrderItemRepresentation.java
│       ├── OrderRepresentation.java
│       └── OrderRepresentationService.java

2、对于一些不隶属于任何业务的代码可以单独分包,比如一些util类、公共配置等。比如我们依然可以创建一个common包,下面放置了Spring公共配置、异常处理框架和日志等子包:

└── common
    ├── configuration
    ├── exception
    ├── loggin
    └── utils

3、异常处理

  在设计异常处理的框架时,需要考虑以下几点:向客户端提供格式统一的异常返回;异常信息中应该包含足够多的上下文信息,最好是结构化的数据以便于客户端解析;不同类型的异常应该包含唯一标识,以便客户端精确识别

   异常两种设计形式:

    1、层级异常:每种具体的异常都对应了一个异常类,这些类最终继承自某个父异常;

    2、全局一个异常:再以一个字段来区分不同的异常场景

  层级异常设计


// ErrorCode枚举中包含了异常的唯一标识、HTTP状态码以及错误信息;而data字段表示各个异常的上下文信息。
public abstract class AppException extends RuntimeException {
    private final ErrorCode code;     
    private final Map<String, Object> data = newHashMap();
}

/// 具体异常
public class OrderNotFoundException extends AppException {
    public OrderNotFoundException(OrderId orderId) {
        super(ErrorCode.ORDER_NOT_FOUND, ImmutableMap.of("orderId", orderId.toString()));
    }
}

在返回异常给客户端时,通过一个ErrorDetail类来统一异常格式:

public final class ErrorDetail {
    private final ErrorCode code;
    private final int status;
    private final String message;
    private final String path;
    private final Instant timestamp;
    private final Map<String, Object> data = newHashMap();
}

样例

{
  requestId: "d008ef46bb4f4cf19c9081ad50df33bd",
  error: {
    code: "ORDER_NOT_FOUND",
    status: 404,
    message: "没有找到订单",
    path: "/order",
    timestamp: 1555031270087,
    data: {
      orderId: "123456789"
    }
  }
}

 4、多环境构建

  在软件的开发流程中,我们需要将软件部署到多个环境,经过多轮验证后才能最终上线。在不同的阶段中,软件的运行态可能是不一样的,比如本地开发时可能将所依赖的第三方系统stub掉;持续集成构建时可能使用的是测试用的内存数据库等等。为此,本文的示例项目推荐采用以下环境:

  local:用于开发者本地开发
  ci:用于持续集成
  dev:用于前端开发联调
  qa:用于测试人员
  uat:类生产环境,用于功能验收(有时也称为staging环境)
  prod:正式的生产环境

 

posted @ 2021-11-03 22:21  飞翔在天  阅读(285)  评论(0编辑  收藏  举报