实体类定义和项目分层的疑惑

算上实习的公司,现在是第四家公司了。头两家公司是传统行业,用的也是经典的三层架构,action,service,Dao。dao层负责持久化;service层负责业务逻辑,事务处理;action负责接收参数和返回数据。我相信只要是java程序员都这样玩过。也曾经疑惑过,为什么要一个简单的CRUD也要还要整俩接口。

还有一个问题是,我们通常会定义的一个实体类和数据库完全匹配,特别是用mybatis的时候,但是当我们返回前台的结果很可能和数据库不一样,这个时候可能需要定义额外的实体类用于匹配查询结果,曾经为了图方便,还用map玩过。

带着这些疑惑稀里糊涂的待了两家公司,终于来到了互联网公司。

同样还是针对一个表的CRUD,尼玛,分了四层,定义了四个实体类,当时我震惊了。后来有人告诉从格局上讲这是SOA、模块化思想,从接口来说,这是领域设计思想。当然架构上除了传统的struts2、spring、mybatis还有dubbo、zookeeper、redis等

我花了一段时间来体会这种架构,简单的说下吧。

如果是对UI显示,层次是这样滴, action , application service,domain service,dao。

如果是对外暴露(dubbo接口),export,application service,domain service,dao。

实体类分为,DTO,DO,VO还有Query(也相当于DTO)

当时项目对application service层简称 ao 层,对domain service层简称manager 层

dao层的作用,大家都知道;manager层用作处理当前领域的业务,它可能是单表,也可能是多表,通常就是当前项目所牵扯的主业务表的增删改查。ao 层就是跨领域层属于应用层,通常需要调其它服务的接口,action层就是接收参数和返回结果,没有任何逻辑。

然后实体类,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的堆积了。

 

当然,好的架构师一点一点摸索,修改出来的,这里只是自己的一些总结。

 

 

 

 

转载于:https://my.oschina.net/everyDay111/blog/670629

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值