分层的本质是关注点分离,隔离对下层的变化,可以简化复杂性,使得层次结构更加清晰。
目前业界存在两种主流的应用工程结构:一种是阿里推出的《Java开发手册》中推荐的,另外一种是基于DDD(领域驱动设计)推荐的。
• 开放 API 层:可直接封装 Service 接口暴露成 RPC 接口;通过 Web 封装成 http 接口;网关控制层等。 • 终端显示层:各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染,JSP 渲染,移 动端展示等。 • Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。 • Service 层:相对具体的业务逻辑服务层。 • Manager 层:通用业务处理层,它有如下特征: 1) 对第三方平台封装的层,预处理返回结果及转化异常信息,适配上层接口。 2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理。 3) 与 DAO 层交互,对多个 DAO 的组合复用。 • DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase、OB 等进行数据交互。 • 第三方服务:包括其它部门 RPC 服务接口,基础平台,其它公司的 HTTP 接口,如淘宝开放平台、支 付宝付款服务、高德地图服务等。 • 外部数据接口:外部(应用)数据存储服务提供的接口,多见于数据迁移场景中。
• 领域层:体现业务逻辑。 • 应用层:依赖领域层,根据业务对下层领域进行聚合和编排。 • 基础设施层:为其他提供技术支持。 • 用户接口层:为外部用户访问底层系统提供交互界面和数据表示。
基于上述两种工程结构,设计一个适合自己的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
/** * 创建订单请求 */ @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); } } }
@Data public class UserBO { private String userId; private String username; private String nickname; }