算上实习的公司,如今是第四家公司了。头两家公司是传统行业,用的也是经典的三层架构,action,service,Dao。dao层负责持久化;service层负责业务逻辑,事务处理;action负责接收参数和返回数据。我相信只要是java程序员都这样玩过。也曾经疑惑过,为何要一个简单的CRUD也要还要整俩接口。java
还有一个问题是,咱们一般会定义的一个实体类和数据库彻底匹配,特别是用mybatis的时候,可是当咱们返回前台的结果极可能和数据库不同,这个时候可能须要定义额外的实体类用于匹配查询结果,曾经为了图方便,还用map玩过。程序员
带着这些疑惑稀里糊涂的待了两家公司,终于来到了互联网公司。redis
一样仍是针对一个表的CRUD,尼玛,分了四层,定义了四个实体类,当时我震惊了。后来有人告诉从格局上讲这是SOA、模块化思想,从接口来讲,这是领域设计思想。固然架构上除了传统的struts二、spring、mybatis还有dubbo、zookeeper、redis等spring
我花了一段时间来体会这种架构,简单的说下吧。sql
若是是对UI显示,层次是这样滴, action , application service,domain service,dao。数据库
若是是对外暴露(dubbo接口),export,application service,domain service,dao。缓存
实体类分为,DTO,DO,VO还有Query(也至关于DTO)mybatis
当时项目对application service层简称 ao 层,对domain service层简称manager 层架构
dao层的做用,你们都知道;manager层用做处理当前领域的业务,它多是单表,也多是多表,一般就是当前项目所牵扯的主业务表的增删改查。ao 层就是跨领域层属于应用层,一般须要调其它服务的接口,action层就是接收参数和返回结果,没有任何逻辑。app
而后实体类,DO是和数据库相匹配的实体,VO是和前台列表相匹配的实体,DTO是接口传输的实体,Query是接口查询条件的实体。
当时作的是CRM系统,我主要参与的就是商家、合同相关的业务。由于当时这个项目,是公司第一个模块化思想的项目,虽然不难作的也很头疼。
以合同查询来讲吧,若是是PC端和手机端来调用的话,入口就是action,传递的参数会包装成Query。由于合同存有城市、商圈、店铺等id、还有附属协议等信息。最开始我直接是一个关联查询,直接通用mybatis匹配成VO,返回到前台。
被个人技术负责人给批了,正确的作法是这样的,我从数据库查出来的数据是个纯净的DO,这个DO通过service(当时咱们是想在这一层用redis作缓存)到ao层,在ao层他会根据城市、商圈、店铺等id分别调不一样的接口去查询相应的信息,而后在组装成VO,返回到action层。
由于当时对服务化这种思想不理解,以为一条SQL就能解决的问题结果工做量瞬间陡增,还让我郁闷好久。
当有其余内部应用要调用这个接口,这个时候走的就是dubbo接口了,经过export接收参数,参数仍是query,若是只是查询单独的合同信息,export会调用service,而后在export层将service返回的DO转成Dto。 若是须要经过这个接口查询合同的关联信息,好比店铺,商家,就调用AO层,
而后将AO层返回的对象转成DTO。
在DO转VO,或者DO转DTO的时候,一般是在VO、DTO定义一个静态工厂方法,经过该方法转换,保证业务代码的纯净。
这就是一个简单的查询接口,虽然开始以为有些绕,后来也以为蛮好,也接触了SOA和领域设计这种思想。这家公司待了没多久,公司C轮融资失败,为了缓解公司的经济危机,我也就跳槽了。而后我就来了这一家公司,一家C轮的电商公司。
仍是SOA的思想,项目包结构依然分四层,export,facade,service,dao。实体类就定义了比较随意了,没有严格的规范。开始我以为这四层跟我上家公司应该相似,只是名字换了而已。结果开发项目的时候却发现不同,也让我对这种分层有了更深的认识。
export层:dubbo暴露的接口,实际功能基本上就是对应app的一个按钮。主要是作异常归集和接口暴露。
facade层:至关于一个门面,接口的主流程,理论上就是一条线,比较干净,抛一个自定义业务异常。
service层:接口的主逻辑,还有校验参数。一般一个接口参数校验应该越早越好,毕竟能早发现错误就早发现错误嘛,可是咱们这边的规范是参数校验在service层。
dao层:很少说了。
由于dubbo接口是不能直接被app调用的,因此咱们有个adaptor,算一个单独的项目,他会把app的http请求,转换成dubbo的入参,而后调咱们的dubbo接口。
说是说的很顺畅,写的时候,我是很疑惑。
export层,它针对的是app的一个功能,对外暴露的接口复用性太差。
facade层,说是接口的业务流程,由于接口的主逻辑在service层,我看到不少人的代码,这一层就是调了一下service,致使这一层很薄,缺乏存在的意义。
service层,参数校验,业务逻辑,调外部应用的接口,组装对象,过重了,彻底不可复用。
dao层,我以前告诫公司的实习生,这里能复用就复用,而后公司的其余同事,却告诫他们能不复用就不复用。dao层的方法过多。
实体类,除了有和表关联的一个实体,入参和出参的实体只和app的功能相关,彻底不可复用。
有一天和同事聊到这个项目,他是负责review 代码的一员,他说了句,"感受大家写的代码都是一次性代码”,虽然我不知道他的理解跟我是否同样,可是我也是深有同感。固然真正开发的时候,不是一些理论所能归纳全的。我总结一下本身作的项目,聊一聊本身的见解吧。
先谈一下实体类
DO,我以前的公司把它定义为什么数据库对应的实体,其实它应该是个domain entity。它包含了数据库的全部字段,同时也包含了这个领域的其余关联对象,由于在一个领域内,可使用sql的关联查询,没必要在为了关联查询新建一个类。
DTO,数据传输对象,DO使咱们从数据库获得的领域对象,而DTO每每也包含了多个领域的信息,因此咱们须要转成DTO。
VO,其实它也属于DTO的一种。
Query,查询入参的后缀,一样属于DTO。
而后谈一下分层
action或者export:它负责入参和出参,对于普通的http应用它就是咱们一般用struts2实现的action、用springMVC实现的controller。对于企业内部,可能不是使用http协议,但它依然负责某个应用的入参和出参。
ao或者facade:它的确是个门面,负责调用本领域的接口,和跨领域的接口
manager或者service:本领域的业务
dao:数据层。
其中Service和Dao层操做的对象理论上只应该有DO和query。
facade和export层操做的对象,理论上只应该有DTO,VO和Query。咱们在facade层作对象的转换,DO转DTO,转VO。固然对象转换的操做最好交由具体的实体类操做。
由于service层和dao层是当前领域的业务,因此它们可能比较薄。业务最好保证必定的原子性,功能上保证松耦合,高复用。
facade层属于应用层,应用层要保证业务逻辑的干净,通用的业务,能够考虑模板。它的变化可能也比较快,固然若是service作的好的好的话,就是service的堆积了。
固然,好的架构师一点一点摸索,修改出来的,这里只是本身的一些总结。