分层的本质是关注点分离,隔离对下层的变化,可以简化复杂性,使得层次结构更加清晰。

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层做聚合编排,比如下单,既要保存订单信息,又要扣减库存,就需要对订单域和库存域进行聚合编排。

Java应用工程结构的更多相关文章

  1. Java Web工程目录结构

    说明 介绍java web 工程的基本结构目录.记录java web 工程结构的学习. Java web 结构 java web严格来说分为两类工程结构:一个是工程编译目录结构,一个是工程发布目录结构 ...

  2. [转]Java Web工程目录结构

    说明 介绍java web 工程的基本结构目录.记录java web 工程结构的学习. Java web 结构 java web严格来说分为两类工程结构:一个是工程编译目录结构,一个是工程发布目录结构 ...

  3. 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...

  4. 重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 程序员‍‍的上下文是什么? 很多时候一大部分编程开发的人员都只是关注于功能的实现,只 ...

  5. 基于IDEA 最新Spirng3.2+hibernate4+struts2.3 全注解配置 登录

    原文 基于IDEA 最新Spirng3.2+hibernate4+struts2.3 全注解配置 登录 首先说说 IDEA 12,由于myeclipse越来越卡,我改用idea12 了,发现其功能强悍 ...

  6. Hadoop2源码分析-准备篇

    1.概述 我们已经能够搭建一个高可用的Hadoop平台了,也熟悉并掌握了一个项目在Hadoop平台下的开发流程,基于Hadoop的一些套件我们也能够使用,并且能利用这些套件进行一些任务的开发.在Had ...

  7. org.thymeleaf.exceptions.TemplateInputException: Error resolving template "/ template might not exist or might not be accessible by any of the configured

    异常现象:在本地打包部署完全没有问题,资源文件也都可以映射上,但是打包成jar包部署到服务器上时,就一直报异常,异常信息如下: 严重: Servlet.service() for servlet [d ...

  8. Spring Cloud feign

    Spring Cloud feign使用 前言 环境准备 应用模块 应用程序 应用启动 feign特性 综上 1. 前言 我们在前一篇文章中讲了一些我使用过的一些http的框架 服务间通信之Http框 ...

  9. flex与j2ee的结合(flex+Spring)

    分类: flex spring2012-04-25 02:11 1262人阅读 评论(1) 收藏 举报 flexspringactionscriptjavapropertiesservlet   目录 ...

随机推荐

  1. CSI 工作原理与JuiceFS CSI Driver 的架构设计详解

    容器存储接口(Container Storage Interface)简称 CSI,CSI 建立了行业标准接口的规范,借助 CSI 容器编排系统(CO)可以将任意存储系统暴露给自己的容器工作负载.Ju ...

  2. MySQL&SQL server&Oracle&Access&PostgreSQL数据库sql注入详解

    判断数据库的类型 当我们通过一些测试,发现存在SQL注入之后,首先要做的就是判断数据库的类型. 常用的数据库有MySQL.Access.SQLServer.Oracle.PostgreSQL.虽然绝大 ...

  3. 07模块化设计之top_down

    一设计功能:(一)用两个分频模块,实现16分频,且让输入a 和b在16个系统时钟内,相与一次. (二)模块化设计思想(结构化思维) 拆分,即把一个系统划分成多个功能模块,控制模块,组合模块.然后从功能 ...

  4. 简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别 ?

    MyISAM: 第 134 页 共 485 页不支持事务,但是每次查询都是原子的: 支持表级锁,即每次操作是对整个表加锁: 存储表的总行数: 一个 MYISAM 表有三个文件:索引文件.表结构文件.数 ...

  5. 如何在Linux Centos上部署配置FastDFS

    一.准备工作: 1.准备下面包文件 -- FastDFS_v5.08.tar.gz -- libevent-2.0.22-stable.tar.gz -- libfastcommon-master.z ...

  6. Thread 类中的 yield 方法有什么作用?

    使当前线程从执行状态(运行状态)变为可执行态(就绪状态). 当前线程到了就绪状态,那么接下来哪个线程会从就绪状态变成执行状态呢?可 能是当前线程,也可能是其他线程,看系统的分配了.

  7. springboot-@EventListener简单用法

    @EventListener简单描述 简化我们编写监听类的步骤,不需要再继承ApplicationListener接口去实现onApplicationEvent了. 例子: @Component pu ...

  8. 用maven建立一个工程5

    在命令行里面输入cd myapp再按回车 再输入mvn compile再按回车 再输入 cd target按回车 再输入cd../按回车 再输入mvn package按回车 最后输入java -cla ...

  9. java接口返回为空时候如何处理

    java前后端分离以后,后端常常返回给前端以下的内容: 如果遇到某个字段的内容为空的时候会出现这样的情况: 图中红色箭头的情况是一个数组集合,但是该集合为空,所以就返回null,但是我们如果想对于这样 ...

  10. 【动态系统的建模与分析】9_一阶系统的频率响应_低通滤波器_Matlab/Simulink分析

    magnitude response:振幅响应 phase response:相位响应 传递函数G(S)为什么将S看成jw化成G(jw)通过[动态系统的建模与分析]8_频率响应_详细数学推导 G(jw ...