问答系统的系统设计方案
一、软件架构风格
“每一个模式描述了一个在我们周围不断重复发生的问题及该问题解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复工作”。模式的关键在于模式的可重复性,问题与场景的可重复性带来解决方案的可重复使用。而架构也与此同义,为了实现系统的高性能、高可用、易伸缩、可扩展、安全等各种技术架构目标,各大互联网公司提出各种解决方案,这些方案被业界复用,从而逐渐形成大型网站架构模式。
分层是企业应用系统中最常见的一种架构模式,将系统在横向维度上切分成几个部分,每个部分负责一部分相对比较单一的职责,然后通过上层对下层的依赖和调用组成一个完整的系统。
分层在计算机世界无处不在,在本项目也用到了,如下图:
主要层分为负责处理请求的api层,负责业务处理的service层,负责数据处理的model层,其他还有负责缓存的cache层,中间件层middleware以及一些配置层等等,具体见下图:
分割就是即是纵向方面对软件进行切分。本系统以问答为核心,粒度适中,比如服务层目前分割成用户,问题,回答等,其中问题又可分割成普通问题和热点问题,其对应页面展示也有所不同。
数据访问热点不均衡,频繁数据放在缓存中,并且有一定期限不会长期占用宝贵内存,通过缓存加快数据访问速度,减轻后端应用和数据存储的负载能力,这种设计对数据库架构十分重要。
本系统采用redis远程字典式缓存服务,将部分热点数据进行缓存,能够快速响应用户对热点内容读取的需求,另外点赞收藏等易变化的部分数据不立即存入数据库,而是通过缓存操作加定时任务,实现弱一致性的数据存取。
系统的安全问题也是架构设计中必须要考虑到的,比如直接使用https进行通信,选择更好的加密算法也是需要考虑好的。本项目主要使用Bcrypt算法。用户表的密码通常使用MD5等不可逆算法加密后存储,为防止彩虹表破解更会先使用一个特定的字符串加密,然后再使用一个随机的salt加密。 特定字符串是程序代码中固定的,salt是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。 BCrypt算法将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题。
二、接口设计风格
谈完了系统的架构风格,我们来谈谈接口设计的策略风格。一个后端系统主要任务就是设计好各种不同任务的底层接口,一个好的接口设计往往利于提高系统的解耦程度,而一个好的设计规范更能方便使用者调用,也利于后端开发人员理解任务的性质。
本系统采用restful风格的API设计规范,即"动词 + 宾语"的结构组成的数据操作指令,主要涉及get(读取)、post(新建)、put(更新)、patch(部分更新)和delete(删除)。
此外,响应状态码也应该精确,这里是内部返回响应,主要包含状态码,提示信息,数据,如下图通用返回结构体:
三、软件系统概念原型下的视图
一个系统无论大小,皆可分模块,通过视图勾划出系统结构,往往会通过不同抽象层级的软件模块形成层次化的结构。
如下图,可知大致功能划分。
各层依赖视图:
关于泛化视图,由于采用的是面向过程的go语言没有类与类的继承等特性,但采用了组合方式来实现各层函数的调用。例如model层需要定义answer结构体,并且为这个实体嵌入一些常用方法,以供service层调用。
关于执行视图,这里以判断用户是否能修改问题的时序图来举例:
也可看看用例图:
部署视图,本系统采用docker部署:
四、软件系统运行环境和技术选型说明
编程语言:go
web框架:gin
orm:gorm
缓存:redis
消息中间件:rabbitMQ
运行环境:Linux+docker
五、数据库设计
设计者的概念模型和最终用户的概念模型。设计模型作为产品设计者的概念模型,是对产品构成结构和操作方式的系统化、结构化描述;相对应的用户模型,是用户在产品使用过程中形成的关于产品构成和操作方式等的结构化理解。而设计者是通过一系列视觉线索,以及用户的产品使用过程将设计模型传递给用户,并最终转换为用户概念模型。设计模型和用户模型的最理想状态就是二者完全等同,即产品的所有设计意图和操作方式完全被用户所理解。因此实体的划分与联系至关重要,由功能结构分析可知,系统需包含用户、用户信息、问题、问题标签、回答、收藏、消息实体,大致E-R图如下:
- 通用字段
列名 | 数据类型 | 长度 | 唯一 | 非空 | 注释 |
id | int | 11 | 是 | 是 | 通用主键 |
created_at | datetime |
| 否 | 否 | 创建时间 |
updated_at | datetime |
| 否 | 否 | 更新时间 |
deleted_at | datetime |
| 否 | 否 | 删除时间 |
- 用户表(user)
列名 | 数据类型 | 长度 | 唯一 | 非空 | 注释 |
username | varchar | 255 | 是 | 是 | 用户名 |
password | varchar | 255 | 否 | 是 | 密码 |
nickname | varchar | 255 | 否 | 否 | 昵称 |
varchar | 255 | 是 | 是 | 邮箱 | |
avatar | text |
| 否 | 是 | 头像 |
status | int | 11 | 否 | 是 | 状态 |
user_profile_id | int | 11 | 是 | 是 | 用户信息逻辑外键 |
- 用户信息表(user_profile)
列名 | 数据类型 | 长度 | 唯一 | 非空 | 注释 |
varchar | 255 | 否 | 否 | 邮箱 | |
phone | varchar | 255 | 否 | 否 | 手机号 |
description | varchar | 100 | 否 | 否 | 个人介绍 |
- 问题标签表(tag)
列名 | 数据类型 | 长度 | 唯一 | 非空 | 注释 |
name | varchar | 255 | 否 | 是 | 名字 |
description | varchar | 1000 | 否 | 是 | 描述 |
icon | varchar | 255 | 否 | 否 | 图标url |
topic_count | int | 11 | 否 | 是 | 所属问题个数 |
- 问题表(topic)
列名 | 数据类型 | 长度 | 唯一 | 非空 | 注释 |
title | varchar | 255 | 否 | 是 | 标题 |
content | text | 1000 | 否 | 否 | 简述 |
user_id | Int | 11 | 否 | 是 | 用户id |
comment_count | int | 11 | 否 | 是 | 评论总数 |
collect_count | int | 11 | 否 | 是 | 收藏总数 |
view | int | 11 | 否 | 是 | 点击量 |
top | bit | 1 | 否 | 是 | 是否置顶 |
up_count | int | 11 | 否 | 是 | 点赞次数 |
down_count | Int | 11 | 否 | 是 | 点踩次数 |
- 标签-问题表(topic_tag)
列名 | 数据类型 | 长度 | 唯一 | 非空 | 注释 |
tag_id | int | 11 | 否 | 是 | 标签id |
topic_id | int | 11 | 否 | 是 | 问题id |
六、项目源代码文件结构
总体如下:
主要层下级目录:
七、概念原型核心工作机制
在系统架构时经常会采用概念原型来使得跟系统相关的所有成员,如客户、用户、开发人员等对系统初步达成一致的理解。其中,分解不同的视图,能狗从不同的视角来掌握整个业务的大致流程。
从上文结合数据库设计可知,本系统主要有用户模型,问题模型,回答模型等主体模型。关于工作过程,匿名用户可以不登陆进行浏览问题、回答和查看他人信息,但是不能进行发布问题回答操作;登录用户可以有全部权限,即登录后可发布问题,回答问题,回复他人,查看账号信息及相关操作等。