[敏捷软工团队博客]技术规格说明书
项目 | 内容 |
---|---|
2020春季计算机学院软件工程(罗杰 任健) | 博客园班级博客 |
作业要求 | 技术规格说明书 |
我们在这个课程的目标是 | 在团队合作中锻炼自己 |
这个作业在哪个具体方面帮助我们实现目标 | 技术功能规格制定与说明 |
概述&技术栈
概述
我们的项目延续了罗继东学长的毕业设计,旨在进一步设计并完善一个基于GitLab的软件工程教学实践平台。为软工课程的个人项目、结对项目和团队项目管理提供一个All In One的解决方案。
技术栈
GitLab相关
GitLab OAuth applications:提供登录服务。
GitLab API
-
GitLab Runner + GitLab CI:每次代码更改后自动编译测试,并将结果发送回GitLab。
-
System Hook + Webhook:监视各类事件(如项目创建,代码签入等),向指定URL发送报告,为同学和教师提供原始数据供分析。
后端
Ruby on Rails
- Haml:渲染Web页面。
- Bundler:提供gem后台应用依赖管理。
- Nginx:为Rails服务提供反向代理。
- Webpacker:集成前端MVVM框架Vue.js与Rails。
数据库
Sqlite3/PostgreSQL:提供development/production双数据库支持,通过gem*切换。
前端
Vue.js
-
Element UI:提供前端UI组件库,绘制网页。
-
Echarts:提供可视化图表支持,构建burn-down chart等。
-
Yarn:提供包管理工具。
-
webpack:前端模块化管理。
-
Babel:编译JavaScript代码。
产品设计
架构
宏观架构
宏观架构设计图
接口规格(暂定)
# | 请求方法 | 请求路径 | 路由指向 | 用途 |
---|---|---|---|---|
1 | post |
/system |
system#index |
添加system-wide webhook |
2 | post |
/webhook |
webhook#index |
给每个project添加webhook |
3 | post |
/report |
report#index |
接受自动测试结果的report接口 |
4 | get |
/login |
sessions#login |
OAuth 认证登录 |
5 | get |
/oauth/callback |
sessions#login |
OAuth 认证登录应用 |
6 | get |
:issues : all |
issues#all_issues |
全量 issues |
7 | get |
:projects : kanban |
boards#index |
project看板 |
8 | post |
:projects : uploads |
uploads#index |
上传文件 |
9 | get |
:projects : labels |
projects#labels |
获取project label |
10 | get |
:projects : members |
projects#members |
获取project成员 |
11 | get |
:milestones : kanban |
boards#index |
获取进度图 |
12 | get |
:milestones : burndown |
burndown#index |
获取燃尽图 |
13 | post |
:classroom : feedback |
auto_test_projects#feedback |
申请人列表 |
14 | post |
:classroom : trigger |
auto_test_projects#trigger |
同意申请 |
15 | get |
:team_projects : raw |
blogs#show_rawn |
展示raw文件 |
16 | get |
:team_projects : insight |
insight#show |
浏览动作 |
17 | get |
:blogs : raw |
blogs#show_raw |
展示博客raw文件 |
18 | get |
member: join |
classrooms#join |
学生加入 |
19 | get |
member: exit |
classrooms#exit |
学生退出 |
分析
抽象与解耦
我们的系统主要基于Vue.js等前端框架以及Ruby on Rails后端框架。这一部分,我们会依照由后到前,由底向上的顺序对我们的系统在通过抽象与模块化实现高内聚低耦合。
-
数据库与后端框架的解耦
-
Ruby on Rails框架将数据库中对应的实体分别转化成为Ruby中的对象,将使用数据库描述语言(DDL)与数据库操作语言(DML)对数据库的创建和操作转化为对实体对应的Ruby对象实例的方法的调用。
-
对于常见的每一种类型的数据库,Ruby on Rails框架都能够提供较为完善可靠的支持。只需要在
Gemfile
文件中指定项目依赖的gem即可,例如:group :development do gem 'sqlite3' end group :production do gem 'pg' end
可以看出,我们在开发环境,为了便捷实用了轻量化数据库Sqlite3,在生产环境将使用PostgreSQL。
-
在Ruby on Rails框架中提供直接使用SQL语言进行数据库查询操作的功能,但是我们不使用这种方法,避免不同数据库之间对于SQL语言处理的差异而导致系统在更换数据库时出现问题。
-
基于以上考虑,我们实现了数据库与后端框架的充分解耦,这样在我们开发环境和生产环境中我们可以实现比较*的切换。例如,在项目目录下
config/database.yml
中,可以*地配置开发环境和生产环境中使用的数据库:development: <<: *default adapter: sqlite3 database: db/development.sqlite3 production: <<: *default adapter: postgresql encoding: unicode database: teach username: deploy password: <%= ENV['TEACH_DATABASE_PASSWORD'] %>
在切换到新的数据库后,只要执行
rails db:migrate
即可自动部署数据库中的实体,灵活性很强。
-
-
后端内部的解耦
- Ruby on Rails框架使用MVC设计模式,即Models、Views、Controllers。其中,Models用于和数据库中的实体进行对应,Controllers用于响应用户的路由请求,Views用于接受Controllers的处理,对页面进行渲染并最终呈现给用户。
- 首先用户发出Request,根据设定的路由表(位于
config/routes.rb
),将请求作为参数传递给相应的控制器,控制器不必关心用户是如何调用其方法的,只要能够在路由需要调用其某一方法时能够给出正确的相应即可。在相应中需要与Models进行交互。然后,只要将渲染页面需要的变量等信息传递给对应的视图Views即可,而不必关心视图如何进行渲染,实现了后端与前端的解耦。
-
后端与前端的解耦
- 正如上文所述,Views部分只接收由Controllers传来的参数,并根据此参数渲染相应的页面。Controllers并不关心Views如何渲染页面。这一设计很好地实现了界面和实现的分离。
- 通常情况下使用带有嵌入式Ruby语句的HTML页面(
*.html.erb
),在我们的项目中使用haml(HTML abstraction markup language),旨在使前端页面代码更加简洁优雅。同时我们使用Vue.js与ElementUI作为前端框架。
系统整体的模型结构如下图所示。
课程平台与GitLab的交互与分离
上一节主要分析了课程平台(即我们的项目的主要部分)内部的交互与分离。由于我们的项目是集成在GitLab中的,因此这一节我们重点介绍本项目与GitLab平台之间的交互与分离。
Webhook
Webhook 是“用户定义的 HTTP 回调”,它由某些事件触发,包括代码 push,创建或修改Issue等等。Webhook属于项目的范畴,在项目的集成设置中配置好Webhook的回调 URL 后,每次发生代码 push 或 Issues 修改等事件后,GitLab 将向该 URL 发起 一个 POST 请求,将相关的数据发送到该 URL(如 push 事件触发时发送本次 push 的 commit 数据)。本文的软件工程教学实践平台通过注册 Webhook 来实现学生 Issues 和 commit 信息的追踪,从而形成贡献报告。
System Hook
与 Webhook 不同,GitLab System Hook 是 GitLab 中全局级别的Hook,当项目创建、更新、删除,用户创建更新、删除,Group 创建、更新、删除时 System Hook 将会被调用,调用方式为向已注册的 URL 发送当前事件的数据(如项目创建时发送项目数 据)。本文的软件工程教学实践平台通过注册 System Hook 跟踪项目创建,从而添加 Webhook。
交互与分离分析
我们的平台主要通过上述的Webhook与System Hook实现从GitLab中获取信息并进行交互的。除此之外,我们对GitLab前端的代码进行了一小处修改,使得能够在GitLab的Dashboard面板中看到我们软件工程平台的入口。通过这种方式,我们将GitLab平台与软件工程课程平台两部分制作为模块之间的调用关系,两个平级的模块相互调用对方提供的API,实现了模块之间的松解耦。
海量数据适应性
对于海量数据的适应性,在我们的项目中主要关注两方面。一方面是静态的用户信息,另一方面是用户与系统的动态交互。
静态的用户信息
这里我们参考了一下网上的一些资料。因为考虑到我们的系统是配合软件工程的教学使用的,因此我们的用户增长通常是以学期为单位的,根据我们上一篇博客的分析,我们的用户量增长是相对缓慢并且前期不会太大(在1000一下的量级),因此这一部分并不是我们要重点考虑的。在此,我们从网上收集的资料进行整理,并结合我们的项目进行分析,如下所示。
-
数据库读写分离
- 这一机制适用于数据IO很大,且存在读性能瓶颈的情况。例如我们使用的GitLab本身即是基于Ruby on Rails框架实现的,Ruby on Rails为敏捷开发而设计,然而其缺点之一在于,如果数据量比较大时相应速度会比较慢,例如我们从GitLab仓库拉取同学们的代码时,同时拉取太多会导致读写性能瓶颈的发生。
- 设置一个主数据库和多个从数据库,主库主要负责写操作,从库负责读操作,且分担掉读压力。
-
服务端分布式
-
引入分布式服务端,包括GitLab与平台的分布等等,进一步增强并行化。
-
由Nginx或者其他解决方案进行负载均衡管理,在我们的系统中已使用Nginx进行反向代理。
-
动态的用户交互
在我们的项目中,为了能够实现自动评测以及方便同学们进行自动部署,我们引入了GitLab CI,帮助同学们对自己的项目实现持续集成,这里我们主要使用了GitLab Runner。GitLab Runner 是基于 Go 语言开发的开源项目,它和 GitLab CI 结合使用,用于运行 CI 任务,并将 CI 任务的结果发送回 GitLab。由于GitLab Runner是可以运行在多个服务器上的,只要在创建Runner时配置GitLab所在的IP地址即可,因此这种设计也为我们在同时有大量同学部署项目或者进行评测时保证Runner的充足,提高并行性,减少用户的Stuck等待时间。
信息封装和隐藏
Ruby on Rails
我们的后端基于Ruby on Rails框架。其中信息的封装与隐藏主要是通过类的封装与访问控制进行实现的。Ruby是一门高度的对象化语言,其设计原则之一即“万物皆对象”。正如前文所述,我们将数据库中的实体信息封装为了Ruby on Rails框架中的对象实例,通过对对象实例的成员变量和方法进行访问控制,即可实现对于底层数据库的信息封装与隐藏。Ruby on Rails框架及其应用的MVC设计模式为信息的封装和隐藏提供了很大的帮助。
GitLab
我们的课程平台是集成于GitLab中的。GitLab的设计已经非常成熟,我们的平台也并不关系GitLab内部工作的细节,而只是通过GitLab开放的API接口与其进行交互,进而实现了GitLab内部信息的封装和隐藏。
Vue.js
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
我们的前端主要基于Vue.js框架,Vue.js框架中一个很重要的概念是组件,不同功能可以封装在不同的组件中,同时可以为其他组件的调用提供交互接口。同时,除此之外我们在前端不免要使用CSS层叠样式表以及JavaScript。在使用过程中我们会尽量避免在HTML页面中出现对于样式的设定,例如在div
标签中指定style
,或者设置某个button
标签的onclick
为一个JS文件中的某个方法等等。取而代之,我们会将这些内容严格封装在CSS和JS中,尽量保证HTML页面的整洁性。
灵活性
- 一方面,如前文所述,我们的项目由于使用了Ruby on Rails框架,实现了数据库与框架的解耦,因此在数据库的更换上具有很强的灵活性。如果需要使用不同的数据库,只要在
Gemfile
中增加对应数据库的gem包依赖,然后执行bundle install
自动安装所有依赖的gem包,并使用rails db:migrate
进行数据库迁移即可。 - 另一方面,我们的项目具有部署的灵活性。我们使用了Webpacker。Webpacker 使 Rails 应用使用、管理和打包 Javascript 更加容易,方便前端组件的开发和调试。我们的项目使用 Webpacker 将前端 MVVM 框架 Vue.js 和 Rails 集成在一起。同时我们还会为使用者提供在本地和在服务器上的部署脚本以及docker镜像,实现真正的一键部署,开箱即用。
错误处理
-
请求地址错误
-
非法请求
- 即由于用户对Request的地址的不合法修改而导致出现了非法的地址。
- 我们在Ruby on Rails框架中的
routes.rb
中的最后设置默认的路由。一旦之前所有请求和地址都没有得到匹配,则会经过此默认路由,将Request交给对应的错误处理Controller进行处理,例如提供一条错误提示信息,然后将用户访问重定向到自己的个人页面的主页,并将此提示信息渲染。
-
越权访问
-
即由于用户有意利用路由访问其他用户的信息或权限的行为。
-
我们在相应的Controller增加相关的权限控制语句,对该行为进行控制,例如:
before_action :correct_user, only: [:list_orders, :destroy, :month_income, :year_income,:monthly, :month_average]
这里我们编写一个方法(上述例子中的
correct_user
),根据用户的Session信息以及其在数据库中对应的存储记录进行身份识别与认证,只有经过认证合法后,才允许访问only
后的方法,不然,对其做类似于非法请求的错误处理,给予其提示信息,告知其无访问权限,将其重定向到自己的主页,并渲染此信息。
-
-
-
数据库存入错误
- 这里主要指的是在填写各类表单并将表单内容对应为实体的一条数据记录后将其存储到数据库的过程。
- 这里我们主要通过完善对表单输入项的合法性检查来尽量使其防患于未然,在此基础上,我们对数据库的访问进行限制,一般情况下尽量通过前端提供的API进行数据的录入,避免直接操作数据库,绕过前端的合法性检查而导致数据库中存入了不合法的数据。
总结
综合上述分析,我们的项目是一个前端基于Vue.js,后端基于Ruby on Rails,同时集成在GitLab中的一个综合性项目。我们在设计上充分考虑了模块间以及层次间的解耦,同时对于灵活性与安全性都进行了考虑与设计。我们会在项目的迭代与开发中将其不断完善,最终将其打造为一个能够真正投入教学应用的软件工程课程平台。