Java應用工程結構


分層的本質是關注點分離,隔離對下層的變化,可以簡化復雜性,使得層次結構更加清晰。

1. 主流分層結構介紹

目前業界存在兩種主流的應用工程結構:一種是阿里推出的《Java開發手冊》中推薦的,另外一種是基於DDD(領域驅動設計)推薦的。

1.1 基於阿里《Java開發手冊》的分層結構

• 開放 API 層:可直接封裝 Service 接口暴露成 RPC 接口;通過 Web 封裝成 http 接口;網關控制層等。
• 終端顯示層:各個端的模板渲染並執行顯示的層。當前主要是 velocity 渲染,JS 渲染,JSP 渲染,移
動端展示等。
• Web 層:主要是對訪問控制進行轉發,各類基本參數校驗,或者不復用的業務簡單處理等。 
• Service 層:相對具體的業務邏輯服務層。 
• Manager 層:通用業務處理層,它有如下特征:
1) 對第三方平台封裝的層,預處理返回結果及轉化異常信息,適配上層接口。
2) 對 Service 層通用能力的下沉,如緩存方案、中間件通用處理。 3) 與 DAO 層交互,對多個 DAO 的組合復用。 
• DAO 層:數據訪問層,與底層 MySQL、Oracle、Hbase、OB 等進行數據交互。 
• 第三方服務:包括其它部門 RPC 服務接口,基礎平台,其它公司的 HTTP 接口,如淘寶開放平台、支
付寶付款服務、高德地圖服務等。
• 外部數據接口:外部(應用)數據存儲服務提供的接口,多見於數據遷移場景中。

1.2 基於DDD(領域驅動設計)的分層結構

• 領域層:體現業務邏輯。
• 應用層:依賴領域層,根據業務對下層領域進行聚合和編排。
• 基礎設施層:為其他提供技術支持。
• 用戶接口層:為外部用戶訪問底層系統提供交互界面和數據表示。

2. 自己的工程結構

基於上述兩種工程結構,設計一個適合自己的Java項目分層結構。

example
└─src
    ├─main
    │  ├─java
    │  │  └─com
    │  │      └─example
    │  │          ├─application                 --應用層(聚合多個領域)
    │  │          ├─domain                      --領域層
    │  │          │  ├─order                      --訂單域
    │  │          │  │  ├─bo                        --業務對象
    │  │          │  │  ├─constant                  --領域內局部常量
    │  │          │  │  ├─controller                --控制器
    │  │          │  │  ├─dto                       --數據傳輸對象
    │  │          │  │  ├─event                     --事件
    │  │          │  │  │  ├─publish                  --發布
    │  │          │  │  │  └─subscribe                --訂閱
    │  │          │  │  ├─manager                   --通用邏輯處理
    │  │          │  │  ├─repository                --存儲
    │  │          │  │  │  ├─entity                   --實體,對應數據庫中的字段
    │  │          │  │  │  └─mapper                   --mybatis mapper
    │  │          │  │  └─service                   --業務層處理
    │  │          │  │      └─impl                    --業務接口實現
    │  │          │  └─user                       --用戶域
    │  │          │      ├─bo
    │  │          │      ├─constant
    │  │          │      ├─controller
    │  │          │      ├─dto
    │  │          │      ├─event
    │  │          │      │  ├─publish
    │  │          │      │  └─subscribe
    │  │          │      ├─manager
    │  │          │      ├─repository
    │  │          │      │  ├─entity
    │  │          │      │  └─mapper
    │  │          │      └─service
    │  │          │          └─impl
    │  │          └─infrastructure             --基礎設施層
    │  │              ├─config                   --配置
    │  │              ├─constant                 --全局常量
    │  │              ├─handler                  --處理器
    │  │              ├─interceptor              --攔截器
    │  │              ├─thirdparty               --第三方
    │  │              └─utils                    --工具類
    │  └─resources        
    │      ├─mapper
    │      │  ├─order
    │      │  └─user
    │      
    │      
    │      
    └─test
        └─java
            └─com
                └─example
  • 接收參數和響應報文,請求以Req為后綴,響應以Resp為后綴,代碼寫在dto包中,比如創建訂單請求和響應
/**
 * 創建訂單請求
 */
@Data
public class OrderCreateReq {

    /**
     * 用戶id
     */
    private String userId;

    /**
     * 訂單金額
     */
    private BigDecimal amount;

    /**
     * 下單的商品集合
     */
    private List<OrderDetailReq> orderDetailReqList;

    @Data
    public static class OrderDetailReq {

        /**
         * 商品id
         */
        private Long goodsId;
        /**
         * 商品數量
         */
        private Integer goodsNum;

    }
}


/**
 * 創建訂單響應
 */
@Data
public class OrderCreateResp {

    /**
     * 訂單id
     */
    private String orderId;
}
  • DAO層代碼放在repository中

  • 業務層代碼放在service和manager中,比如創建訂單因為涉及到訂單表和訂單明細表,需要在一個事務中,所以將事務代碼下沉到manager。

@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderManager orderManager;

    @Override
    public OrderCreateResp create(OrderCreateReq req) {

        Order order = buildOrder(req);
        List<OrderDetail> orderDetailList = buildOrderDetailList(order.getOrderId(), req);

        orderManager.createOrder(order, orderDetailList);

        OrderCreateResp resp = new OrderCreateResp();
        resp.setOrderId(order.getOrderId());
        return resp;
    }


    private Order buildOrder(OrderCreateReq req) {
        Order order = new Order();
        order.setOrderId(UUID.randomUUID().toString());
        order.setUserId(req.getUserId());
        order.setAmount(req.getAmount());
        return order;
    }


    private List<OrderDetail> buildOrderDetailList(String orderId, OrderCreateReq req) {
        List<OrderDetail> orderDetailList = new ArrayList<>();
        for (OrderCreateReq.OrderDetailReq orderDetailReq : req.getOrderDetailReqList()) {
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(orderId);
            orderDetail.setGoodsId(orderDetailReq.getGoodsId());
            orderDetail.setGoodsNum(orderDetailReq.getGoodsNum());
            orderDetailList.add(orderDetail);
        }
        return orderDetailList;
    }
}
@Component
public class OrderManager {

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private OrderDetailMapper orderDetailMapper;

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void createOrder(Order order, List<OrderDetail> orderDetailList) {
        orderMapper.insert(order);
        for (OrderDetail orderDetail : orderDetailList) {
            orderDetailMapper.insert(orderDetail);
        }
    }


}
  • 業務對象存放在bo包中,比如查詢用戶信息,不需要返回密碼字段,則可以定義一個UserBO。
@Data
public class UserBO {
    
    private String userId;
    
    private String username;
    
    private String nickname;
}

  • application層做聚合編排,比如下單,既要保存訂單信息,又要扣減庫存,就需要對訂單域和庫存域進行聚合編排。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM