• 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层做聚合编排,比如下单,既要保存订单信息,又要扣减库存,就需要对订单域和库存域进行聚合编排。
  • 相关阅读:
    粒子群算法(主要针对连续型函数优化问题)
    Qt 编译错误:C2228: “.key”的左边必须有类/结构/联合
    高可用集群HA、LVS+Keepalived、健康检测
    建筑能源管理(5)——建筑能源审计和审计方法
    BUUCTF [GXYCTF2019]佛系青年 1
    Redisson分布式锁
    计算机网络(文章链接汇总)
    Pr怎么消除人声?三个方法解决!
    Linux ubuntu 服务器部署详细教程
    昇思25天学习打卡营第15天 | Vision Transformer图像分类
  • 原文地址:https://www.cnblogs.com/luxh/p/16113512.html