Admin-Framework(一)介绍

点击查看TCSF开发手册目录

 

一、Admin-Framework架构介绍

    Admin-Framework是一个管理后台框架,包含了常规的用户管理、权限管理、资源管理以及系统监控等后台管理功能。整体架构如下图:

    上图中,每一块都是一个Maven模块工程,Commons模块工程封装了通用Service、Controller以及常用的工具类库。在此基础之上,所有的后台管理功能业务逻辑都放置在Admin-Module模块工程。直接面向用户的界面资源以及Web层控制器代码则放在了Admin-Web模块工程中。Admin-Web依赖Admin-Module,Admin-Module依赖于Commons,这三个工程组成了Admin-Framework的核心。

    图中两个绿色方块中表示的是Admin-Framework对扩展提供的支持。TCSF已为开发者提供了两个Maven骨架用于快速创建项目工程,两个Maven骨架就是通过Extension-Web和Extension-Module进行扩展。

    如果是小型规模、模块单一的开发项目,只需要通过admin-demo-web骨架创建Web项目即可,生成的Web项目将会自动通过Extension-Module、Extension-Web具有访问Admin-Module模块和覆盖Admin-Web的能力。

    在业务逻辑稍微复杂的项目中,往往都需要划分为多个模块。开发者通过admin-demo-web骨架创建Web项目之后,可以再通过admin-demo-module骨架创建模块项目将项目模块划分为多个maven模块项目,这有利于模块职责的划分以及开发人员任务的隔离。

 

二、模块工程结构介绍

    Admin-Framework框架对工程的划分原则是:先进行业务的垂直划分,再进行层次的水平划分。业务垂直划分可以形成模块,包括模块工程,或者在一个模块工程内垂直划分形成子模块包,划分为模块工程还是模块包这取决于业务划分的粒度,需要系统设计者自行掌握。层次的水平划分则是遵循分层思想,在骨架创建生成后的模块项目默认采用三层架构,即数据持久层、业务逻辑层和表现层。层次划分并没有强制约束,常见的还有加入门面层的做法,系统设计者可根据需要自行扩展。

1. Commons模块

    TCSF的最基础模块是Commons,Commons包含了通用Service、Controller以及常用的工具类库,所以Commons模块工程内划分了两个commons包和utils包。由于Commons工程依赖了大量服务端开发才会使用的Jar包,所以utils包的工具类是针对于TCSF使用的,目前暂不打算对其他基于Java平台的应用开发进行通用化,utils包将会在TCLIB中进行介绍。

    在commons包中,为其他模块工程定义了通用的Service以及Controller。如下图:

    TCSF中默认模块内是按照三层架构进行水平划分,在commons包内TCSF提供了IdEntity作为数据库实体类的抽象父类,并定义了IQueryService、IGenericService两个服务层接口分别对应于查询服务和CRUD服务。

 

1.1 Idable及IdEntity

    下图展示了在实际项目开发中,Idable及IdEntity所处的位置:

    SysUserGroup、SysUser和SysRole三个实体都分别继承于IdEntity,IdEntity实现了Idable接口中的getId()方法,IdEntity是TCSF中所有数据表实体的父类,它抽象出了所有数据表实体的公共字段:id、createdTime、updatedTime和dataState。在TCSF开发中,数据库设计时强制约定了每一张数据表都必须包含这四个字段,而数据库视图则没有这个要求。之所以抽出这四个字段,是因为这四个字段较为常用,并且抽象到父类也便于在Service层进行处理,例如在插入或更新数据时将自动更新创建时间和更新时间,并标记dataState为启用状态。

    Idable接口约定了所有实现此接口的类必须具有id属性,除了IdEntity实现了Idable以外,系统中所有表单类都将实现此接口,这主要作用是在于Service层可以完成表单类型到实体类型的转换。

 

1.2 通用Service

    commons包中,定义了服务层和控制器层的接口及抽象,其中的关系如下:

    上图中除了Mapper<T>是开源项目“通用Mapper”的类,其余都在Commons模块工程的commons包内。IQueryService<T>定义了单表或视图的查询服务接口,IQueryIdEntityService<T>接口继承了IQueryService<T>接口扩展了针对继承于IdEntity实体的查询操作,IGenericService<T,F>继承了IQueryIdEntityService<T>拥有了查询服务能力后还定义了单表的增删改操作接口。AbstractQueryService<T>、AbstractQueryIdEntityService<T>和AbstractGenericService<T,F>分别继承于IQueryService<T>、IQueryIdEntityService<T>和IGenericService<T,F>,实现了增删改查所有操作。当开发者需要实现单表的增删改查时,仅需要继承AbstractGenericService<T,F>并指定泛型类型即可。

    也许你阅读到此处为感到好奇:为什么要把查询和增删改分离在不同的接口内?

    之所以将查询和增删改分离在不同的接口里,是因为TCSF中定义的通用服务是针对数据库单表操作的,但在实际项目开发中多表查询十分常见,可能多数开发者会在Service层自行编写逻辑代码进行多表联合查询,若业务逻辑较复杂可能会导致开发者要编写许多查询代码。而在TCSF中,只要查询逻辑稍微复杂便推荐开发者创建一个数据库视图,从数据库视图生成视图查询代码,生成的查询代码将会继承AbstractQueryService,也就是说,视图仅提供查询服务,数据表才会提供完整的增删改查操作服务。

    下图为IQueryService<T>和AbstractQueryService<T>的实现关系以及列出了其中所支持的查询方法:

    从上层调用的开发者角度上看,他们仅需关心IQueryService所提供的接口。接口中的方法从名字上看很明了:countAll->统计记录数量,findOne->查询一条记录,findAll->查询所有记录,findPage->分页查询。可能较为难以理解的是Example和Map<String,String>,这两个类型都是组织查询条件。如果你使用过Mybatis Generator从数据库表中逆向生成过代码,你应该一眼就明白了Example的作用,Example就是一个组装查询条件的对象,你可以调用它例如example.or().andEqualTo("name", "张三")来查询名字叫张三的数据库记录。而Map<String,String>是另外一个查询条件的组装方式,这是由TCSF框架定义的。之所有使用Map<String,String>是因为TCSF有涉及使用代码生成器生成页面,页面上的条件组合查询需要十分灵活的条件查询组装方式,TCSF这个特性受启发于SpringSide开源项目,这在后面会进行详细介绍。

    下图列出了IQueryIdEntityService<T>接口所提供的方法:

    IQueryIdEntityService<T>接口提供了IdEntity实体特有的查询服务。IdEntity实体中都具有一个dataState属性用于标识当前实体的状态,dataState属性目前分为三种状态:enable、disable和delete。开发者可使用IQueryIdEntityService<T>中提供的方法快速查询或者统计不同状态的实体和列表。

    下图为IGenericService<T,F>和AbstractGenericService<T,F>的实现关系以及列出了其中所支持的数据库操作方法:

    在IGenericService接口中定义的方法较多,约有30个左右。但仔细查看一下方法名称,你会发现大多方法的功能是一样的,只不过有的方法参数形式不太一样,例如删除实体,你可以使用实体ID删除,也可以使用实体对象删除,从单个实体删除上又扩展出了批量删除,批量删除又支持列表批量删除和数组批量删除。把上图的方法归类一下也不多:创建、更新、删除、禁用、启用、标记删除,就6种功能,但已经覆盖了数据库的所有操作,开发者利用它们足以开发出许多功能。

    在上图中,你应该看到了IGenericService接口对于创建和更新都提供了*ByForm形式的方法。之所以提供了ByForm方法,是因为多数的数据记录创建和更新,并不需要用户传入表中所有字段对应的值,例如一个用户表具有(ID,创建时间,更新时间,数据状态,用户名,密码),创建一个用户仅需要传入用户名和密码两个参数即可,其余字段程序将会自动处理,暴露过多的字段会导致调用者难以理解参数的意义,编写出多余的无效的代码。所以你可以将*ByForm方法参数的类型理解为实体类字段的子集,这个子集以一个新的类型来表示。

 

1.3 通用Controller

    在commons中,还有两个与IQueryService、IGenericService对应的控制器类:QueryController<T,V>和GenericController<T,F,V>。名如其类,QueryController负责提供单数据表或视图的Web查询接口,GenericController负责单数据表的Web增删改操作接口。

    下图展示了QueryController和GenericController两个类之间的关系以及列出了所提供的方法:

    QueryController提供了三个查询接口:get(Long id)、list(Map<String,String> searchParams)和pageList(Map<String,String> searchParams,Integer page,Integer size),功能分别是根据ID获取数据实体,不分页条件查询和分页条件查询。在前面提到了IQueryService中的Map<String,String>查询条件参数就是从这个入口传递的。

    GenericController提供了增删改以及启用禁用的操作接口,在一个继承于GenericController的控制器类中,这些接口都是针对单表操作的。

    当开发者需要对单表操作编写控制器时,可以直接继承于GenericController,便直接拥有了提供操作接口的能力而无需再编写控制器实现代码。若该控制器仅仅为了提供给视图进行查询,除了前面提及的创建Service类继承AbstractGenericService抽象类外,再编写一个继承于GenericController的控制器类,便能实现提供视图查询接口的功能。

    相对于IGenericService<T,F>,GenericController<T,F,V>多了一个泛型V,这个V代表了View Object。由于GenericController是针对单表操作,然而实际项目开发中通常在一个页面里显示的数据往往需要关联到其他表进行查询。所以在QueryController类里便已经定义了泛型V,用于指定界面上显示的数据对象。当使用了QueryController的查询接口,开发者还需要覆写entity2Vo(T t)来编写entity转换为view object的转换规则,以此来达到多表查询的效果。

 

1.4 领域模型

    前面提到的IGenericService<T,F>和IQueryController<T,F,V>,T代表实体Entity,F代表表单对象Form Object,V代表View Object。这三者之间是具有联系的,如下图:

    Form Object中聚集了对一个实体创建、更新所需填写的字段,避免将实体的所有字段都暴露给用户,导致用户编写多余无用的代码。当Form Object传递到Service层,Service将会对Form Object进行处理转换为Entity并通过Mapper持久化到数据库。除了在创建更新中使用Form对象,在另外一种情况下也有使用到Form,就是条件组合查询。当用户需要进行数据查询时,Service层可以通过Form对象来暴露提供给用户的查询条件,避免用户通过Example或者Map<String,String>随意填写条件。

    当页面需要进行数据查询时,页面会调用Controller层的查询接口,如果没有设定View Object时,Controller将会把实体的所有数据返回到页面上。为了避免向页面暴露出敏感字段和为开发者提供可自定义显示字段的灵活性,TCSF在Controller加入了Entity转换为View Object的特性。当Service从数据库查询出实体交给Controller后,Controller可以自行将Entity进行转换为页面上所需的数据对象。

    Entity、View Object以及Form Object在模块层次中的位置如下图所示:

 

1.5 SimpleService和SimpleController

    在某些情况下,开发者可能不需要定义Form Object和View Object,IGenericService<T,F>和GenericController<T,F,V>提供了多个泛型让用户填充,看起来代码上并不是十分简洁,TCSF提供了SimpleService<T>和SimpleController<T>屏蔽了多个泛型,默认以实体类型来填充所有泛型,开发者仅需继承SimpleService<T>和SimpleController<T>即可。

 

2.数据持久层

    在服务端应用开发中,归根结底主要工作是增删改查,TCSF中采用了Mybatis作为数据持久层框架。Hibernate无疑是一个优秀的ORM框架,为了更好的支持面向对象开发方法,Hibernate做出了许多复杂的机制,但这些机制多数是为了弥补对象-关系自动映射所产生的缺点。Mybatis在映射方面并没有Hibernate做的智能,但是却能提供极大灵活的定制特性,开发者可以轻易的对sql进行调优,为了降低代码编写工作量,Mybatis也提供了一款生成器Mybatis-Generator。但是在TCSF中直接使用官方的Mybatis和生成器并不能降低工作量,TCSF实际是采用一款国人开源的基于Mybatis的封装库——通用Mapper。

    通用Mapper将Mybatis Generator生成的Mapper和Example进行了抽象,通过通用Mapper代码生成器生成的mapper配置文件将不在出现大量的sql语句,这些都由通用Mapper动态生成,保持了mapper文件的简洁。包括官方生成器会为每个实体生成Example,通用Mapper中都抽象为一个Example,并且生成的实体都继承于IdEntity,这对TCSF封装通用Service和通用Controller的支持十分有利。

    TCSF基于通用Mapper的Generator自己扩展出了一个代码生成器,开发者完成数据库的设计之后,可以使用代码生成器根据数据表一次性生成Entity、Mapper、mapper配置文件、单表操作Service、单表操作Controller、Form Object、View Object以及后台管理界面。当然,生成的代码具有一定的局限性,生成的每一个代码层次、界面都是预先定义好的模板样式,开发者可能需要在生成代码的基础之上再进行修改才能满足用户需求,但这已经大大节省了开发者许多时间,TCSF的设计目标就是支持开发者快速构建服务端程序,并尽量为开发者提供最大的灵活定制特性,让TCSF开发者最大程度的减少加班,有充足的时间回家陪老婆孩子。

 

3.前端开发

    在多人分工合作的项目开发中,往往首先划分的形式是前后端分离,让前后端开发人员专注于自己的技术领域,之所以先按照前后端划分是因为前后端技术领域有跨度,很难有人既精通前端技术又擅长后端开发。如果先按照业务功能来划分,每个人负责各自模块的前台到后台,极有可能导致前端体验做的不好,后端代码也写的不健壮。

    近几年,Web前端技术发展十分迅猛,前端开发已经不是过去依靠编写html、简单的样式和会使用jquery那么简单了。mvc模式已经发展到mvvm时代,angularjs、vuejs和avalonjs等mvvm框架如雨后春笋般生长。

    在TCSF中,对于前端开发应用了avalon来支持前后端的完全分离。过去的mvc模式,也是号称前后端分离,但还是存在一定的局限性。过去设计界面,是由前端开发人员在dw、webstorm等工具编写完成后,交给后端开发人员嵌套入JSP/Freemarker中,这就要求后端人员也掌握前端开发知识。当需要对页面进行修改时,让前端开发者再修改网页,后端人员重新嵌套从工作量上看不太现实,往往可能会让前端开发人员安装Eclipse等后台开发环境,由前端开发人员在上面进行修改。如果前端和后端开发都是同一个人,工作起来会轻松一些,但后期如果需要切换界面模板,这将会是一个痛苦的过程。

    为了解决这个问题,TCSF中采用了avalon来完成pc网页的界面渲染和数据,后台仅提供数据交互接口,项目开发时前端可以使用Mock Server来模拟后台数据接口,做到前后台完全分离,并行开发,前后端无需相互等待。采取这个开发方式,可以极大的提高工作效率,但也存在一个缺点:对SEO支持不好。因为搜索引擎蜘蛛在抓取网页时,前端通过后端数据接口获取数据来渲染页面,蜘蛛可能会抓取不到这些数据。但在管理系统的项目中,其实并不需要做seo,这个问题我们完全可以忽略。如果有项目需要做seo优化,说明这个项目是一个互联网应用,并且用户规模可能持续增长。在这种情况下,技术开发上应该加大投入,如今nodejs做页面渲染的方案已经在淘宝中进行应用,淘宝在国内的用户规模首屈一指,TCSF允许服务端部署nodejs来进行页面渲染,这一块的工作也交给前端开发人员,如此既能达到前后端工作分离的目的,也能解决seo优化的问题。

 

4.接口开发

    无论是网页开发、APP开发还是windows客户端开发,都会和服务端产生数据交互。在开发之前,往往需要先定义通信接口,之后便是服务端和客户端开发人员根据接口编写接口实现代码,最后进行接口联调。服务端通常都是采用一个平台进行开发,而客户端往往可能是多种多样的,例如:Android App客户端、网页、微信端、IOS app客户端、windows客户端等等。多数情况下,客户端功能可能是一模一样的,但每个平台都需要对接口进行一遍实现编码和调试,这其中耗费的工作量可想而知是十分巨大的。现如今流行app中嵌入网页来减少多平台客户端的重复功能编写工作量,以及编写一个微信小程序就能在android、IOS平台两个平台进行使用,但这两种方式还是存在很大的局限性。app嵌入网页的形式是牺牲了界面体验和性能来换取跨平台的特性,并且有些与操作系统深度交互的工作是网页无法完成的。微信小程序依赖于微信app,在应用场景上局限性较大。

    TCSF中,对于接口开发这一块则是选择拥抱开源项目Swagger。Swagger提供了一个接口编辑器,使用yaml编写接口文档即可生成服务端接口代码、客户端接口调用代码和接口文档。并且Swagger对平台的支持十分全面。

    Swagger支持如下服务端开发平台:

    Swagger支持如下的客户端开发平台:

    使用Swagger开发接口,可以省去了重复的接口代码编写工作以及繁琐的接口调试工作中。唯一的使用门槛就是需要学习Swagger的文档编写语法,这对程序员来说并不是什么难题。并且Swagger提供了Swagger UI,服务端开发人员可以通过这个接口网页可视化的测试服务端接口的正确性。客户端开发人员也能够使用Swagger生成的mock数据在服务端未开发完成的情况下使用Mock Server模拟服务端接口,实现前后台的并行开发。

 

三、心理框架

    开发框架是死的,框架的使用方式却是灵活的。TCSF定义了开发人员如何正确的使用框架,避免开发人员以不同的认知错误的应用框架导致框架的意义被抹灭。

    TCSF定义运用领域驱动设计(DDD)和测试驱动开发的理念来指导开发者使用开发框架,区别于开发框架,我们称此为心理框架。这一套心理框架以Scrum作为骨架,以极限编程(XP)作为实践的指导方针,配合相应的敏捷开发管理工具,大到指导着项目应该如何进行,小到引导程序员如何编写代码,它贯穿了项目开发管理的全过程,让项目开发能够交付出令客户最满意的成果。

    TCSF心理框架介绍篇幅较长,并且也是需要长期积累、总结和改进,所以本文仅仅提及,不做深入的讨论,这将会在后续文章中进行深入介绍。

转载于:https://my.oschina.net/souljava/blog/877766

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值