经过前篇需求梳理,商场停车收费业务需求情况已经十分明了,本节就依据前文的输出做为输入,开始系统设计工作,包括功能模块设计、存储设计、架构设计等,为后面的编码提供良好的基础保障。

有同学可能会有疑问,都使用敏捷了,怎么还要设计,直接上手编码不就行了?敏捷提倡响应变化,减少文档,很多朋友有误解,以为敏捷就是不需要设计,不需要文档,就是要快,凡是阻碍实施交付的都要省掉。其实,敏捷并不是消灭文档,消灭设计,关键性的文档、图片、设计还是要留存的,比如存储设计、关键业务流程设计等,但并不局限于是文档形式,可以是白板上或 A4 纸上的草图,也可以是便利贴,也可以是正式的文档等,能表达意思即可,方便后期追溯。

数据实体联系

基于以上业务情况,按领域划分为七个小模块,每个模块中划分出相应实体、事件,通过软件简略画出关键数据实体-联系图(未包含所有实体),如下图所示:

  1. 会员,车辆,月卡(绑定手机号,录入车辆,开月卡)
  2. 车位,闸机(车辆停靠、车辆离开)
  3. 积分(签到、兑换)
  4. 计费规则(入场、出场)
  5. 交易流水(支付、充值)
  6. 消息(推送)
  7. 洗车

业务模块设计

据第一篇需求分析的情况,我们已经识别出关键流程、主要业务模块以及模块中主要的业务实体、实体相关的事件。本案例完全可以采用单实体的模式开发,但为了模拟微服务开发的场景,所以这里按照微服务的设计方式来进行。

根据关键业务实体联系与事件,将业务模块整合为七个子服务。

  • 会员服务,包括会员信息、车辆信息、会员月卡
  • 基础资源服务,包括车位、闸机,车辆停靠记录
  • 计费服务,车辆出入场记录,计费规则
  • 积分服务,积分兑换,会员积分,会员签到得积分
  • 财务服务,支付流水,充值流水,财务统计
  • 消息服务,记录通知内容
  • 洗车服务,积分兑换的洗车券去场内洗车

服务的拆分粒度究竟多细,业界并没有统一的标准,必须依据公司团队情况、人员能力水平以及产品的使用情况来划分,不可过细,过粗也失去了微服务的意义。 每个微服务可交由二到三个开发人员维护,避免维护过多,分散精力,同时又可保证快速的响应维护升级。

存储设计

微服务架构风格的一个好处,是持久性的封装。我们可以根据每个服务的需要,去选择不同的持久化技术。根据每种数据类型的特点而去选择数据存储的方法,也就是混合持久化,结构化存储与非结构化存储混合使用。不同服务间使用不同的存储模型,单一服务中也可以使用混合存储。既然要采用微服务化结构,从独立开发、运行、部署和运维都是单独的小应用。每个小应用内部业务逻辑处理,到数据库访问,以及数据库都是独立的。

依据本案例的业务场景,我们拆分为七个子存储库,分别为:

  • park_member——会员服务库
  • park_resource——停车场资源服务库
  • park_charging——计费服务库
  • park_card——积分服务库
  • park_finance——财务服务库
  • park_message——消息服务库
  • park-carwash——洗车服务库

实际中有些实施微服务的团队,将服务拆分,但存储库依旧仍是一份,现实中应该有为数不少的存在。不能说不对,只能说不符合微服务的建议。

结构化存储采用社区版 MySQL 5.7+版本,非结构存储主要涉及到缓存这块,采用 Redis 4.0 +版本。结构化存储中建议设计一些通用字段,主要用于跟踪数据记录,库表结构通用字段如下:

  `create_by` varchar(32) DEFAULT NULL COMMENT '创建人',`create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',`update_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新日期',`remark` varchar(500) DEFAULT NULL COMMENT '备注',`version` int(4) DEFAULT '0' COMMENT '版本',`state` int(4) DEFAULT '1' COMMENT '状态'

每条数据记录的创建人、创建时间,后续的更改人、更改时间,非业务层面的备注、版本、状态,有利于数据维护人员识别,建议每个表中都加上。

建库脚本如下

CREATE DATABASE `park_member` CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE DATABASE `park_resource` CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE DATABASE `park_charging` CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE DATABASE `park_card` CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE DATABASE `park_finance` CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE DATABASE `park_message` CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE DATABASE `park_carwash` CHARACTER SET utf8 COLLATE utf8_general_ci;

park_member 库初始化表结构

-- ----------------------------
-- Table structure for member
-- ----------------------------
DROP TABLE IF EXISTS `member`;
CREATE TABLE `member` (`id` varchar(32) NOT NULL DEFAULT '',`phone` varchar(11) DEFAULT NULL COMMENT '手机号',`birth` varchar(10) DEFAULT NULL COMMENT '生日',`full_name` varchar(20) DEFAULT NULL COMMENT '姓名',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员信息';-- ----------------------------
-- Table structure for month_card
-- ----------------------------
DROP TABLE IF EXISTS `month_card`;
CREATE TABLE `month_card` (`id` varchar(32) NOT NULL DEFAULT '',`card_no` varchar(20) DEFAULT NULL COMMENT '会员卡号',`start` varchar(16) DEFAULT NULL COMMENT '有效期起始',`ends` varchar(16) DEFAULT NULL COMMENT '有效期截止',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员月卡信息';-- ----------------------------
-- Table structure for vehicle
-- ----------------------------
DROP TABLE IF EXISTS `vehicle`;
CREATE TABLE `vehicle` (`id` varchar(32) NOT NULL DEFAULT '',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`plate_no` varchar(10) DEFAULT NULL COMMENT '车牌号',`vehicle_inf` varchar(50) DEFAULT NULL COMMENT '车辆型号',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员车辆';

park_resource 库初始化表结构

-- ----------------------------
-- Table structure for brake
-- ----------------------------
DROP TABLE IF EXISTS `brake`;
CREATE TABLE `brake` (`id` varchar(32) NOT NULL DEFAULT '',`code` varchar(20) DEFAULT NULL COMMENT '编号',`desc` varchar(50) DEFAULT NULL COMMENT '描述',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='停车场闸机';-- ----------------------------
-- Table structure for stall
-- ----------------------------
DROP TABLE IF EXISTS `stall`;
CREATE TABLE `stall` (`id` varchar(32) NOT NULL DEFAULT '',`code` varchar(10) DEFAULT NULL COMMENT '编号',`is_parked` int(2) DEFAULT NULL COMMENT '是否被占用',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车位表';-- ----------------------------
-- Table structure for stall_parked
-- ----------------------------
DROP TABLE IF EXISTS `stall_parked`;
CREATE TABLE `stall_parked` (`id` varchar(32) NOT NULL DEFAULT '',`stall_id` varchar(32) DEFAULT NULL COMMENT '车位编号',`plate_no` varchar(30) DEFAULT NULL COMMENT '车牌',`mtype` int(2) DEFAULT NULL COMMENT '0 入场,1 出场',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车位停靠记录';

park_charging 库初始化表结构

-- ----------------------------
-- Table structure for charging_rule
-- ----------------------------
DROP TABLE IF EXISTS `charging_rule`;
CREATE TABLE `charging_rule` (`id` varchar(32) NOT NULL DEFAULT '',`start` int(4) DEFAULT NULL COMMENT '停车时间起始',`end` int(4) DEFAULT NULL COMMENT '停车时间结束',`fee` float DEFAULT NULL COMMENT '收费',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='计费规则';-- ----------------------------
-- Table structure for entrance
-- ----------------------------
DROP TABLE IF EXISTS `entrance`;
CREATE TABLE `entrance` (`id` varchar(32) NOT NULL DEFAULT '',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`plate_no` varchar(10) DEFAULT NULL COMMENT '车牌',`brake_id` varchar(32) DEFAULT NULL COMMENT '闸机号',PRIMARY KEY (`id`) USING BTREE,KEY `no_idx` (`plate_no`),KEY `member_idx` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆入场';-- ----------------------------
-- Table structure for vexists
-- ----------------------------
DROP TABLE IF EXISTS `vexists`;
CREATE TABLE `vexists` (`id` varchar(32) NOT NULL DEFAULT '',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`brake_id` varchar(32) DEFAULT NULL COMMENT '闸机号',`plate_no` varchar(32) DEFAULT NULL COMMENT '车牌号',PRIMARY KEY (`id`) USING BTREE,KEY `no_idx` (`plate_no`),KEY `member_idx` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆驶出';

park_card 库初始化表结构

-- ----------------------------
-- Table structure for exchange
-- ----------------------------
DROP TABLE IF EXISTS `exchange`;
CREATE TABLE `exchange` (`id` varchar(32) NOT NULL DEFAULT '',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`card_qty` int(11) DEFAULT NULL COMMENT '积分数量',`ctype` int(4) DEFAULT NULL COMMENT '0 优惠券,1 洗车券',`code` varchar(30) DEFAULT NULL COMMENT '券编码',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分兑换';-- ----------------------------
-- Table structure for member_card
-- ----------------------------
DROP TABLE IF EXISTS `member_card`;
CREATE TABLE `member_card` (`id` varchar(32) NOT NULL DEFAULT '',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`cur_qty` varchar(20) DEFAULT NULL COMMENT '当前可用积分',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员积分';-- ----------------------------
-- Table structure for member_sign
-- ----------------------------
DROP TABLE IF EXISTS `member_sign`;
CREATE TABLE `member_sign` (`id` varchar(32) NOT NULL DEFAULT '',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`cnt` int(4) DEFAULT NULL COMMENT '积分数量',`ctype` int(4) DEFAULT NULL COMMENT '0 签到,1 商场消费',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员签到';

park_finance 库初始化表结构

-- ----------------------------
-- Table structure for billing
-- ----------------------------
DROP TABLE IF EXISTS `billing`;
CREATE TABLE `billing` (`id` varchar(32) NOT NULL DEFAULT '',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`fee` float DEFAULT '0' COMMENT '支付金额',`plate_no` varchar(10) DEFAULT NULL COMMENT '车牌号',`duration` float DEFAULT '0' COMMENT '停车时间',PRIMARY KEY (`id`) USING BTREE,KEY `no_idx` (`plate_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆驶出支付流水';-- ----------------------------
-- Table structure for month_card_recharge
-- ----------------------------
DROP TABLE IF EXISTS `month_card_recharge`;
CREATE TABLE `month_card_recharge` (`id` varchar(32) NOT NULL DEFAULT '',`card_no` varchar(20) DEFAULT NULL COMMENT '月卡号',`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`amount` float DEFAULT NULL COMMENT '充值金额',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员月卡充值';

park_message 库初始化表结构

-- ----------------------------
-- Table structure for message
-- ----------------------------
DROP TABLE IF EXISTS `message`;
CREATE TABLE `message` (`id` varchar(32) NOT NULL DEFAULT '',`mtype` char(10) DEFAULT NULL COMMENT '消息类型,PAY 支付消息,BIND 绑定信息',`mcontent` varchar(500) DEFAULT NULL COMMENT '消息内容',`member_id` varchar(32) DEFAULT NULL COMMENT '会员',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='推送消息';

park_carwash 库表结构

-- ----------------------------
-- Table structure for car_wash
-- ----------------------------
DROP TABLE IF EXISTS `car_wash`;
CREATE TABLE `car_wash` (`id` varchar(32) NOT NULL,`member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',`plate_no` varchar(10) DEFAULT NULL COMMENT '车牌号',`ticket_code` varchar(20) DEFAULT NULL COMMENT '洗车券编码',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

完整的表结构脚本地址,点击下方链接:

https://github.com/backkoms/spring-cloud-alibaba-ParkingLot/tree/master/src/script

初始化数据

有了初步数据库的模型,需要初始化一部分数据进去,比如计费规则、闸机信息,车位信息。

闸机数据

INSERT INTO `brake` VALUES ('4edb0820241041e5a0f08d01992de4c0', 'ct1', '入场口', 'admin', '2019-12-27 11:37:22', NULL, '2019-12-27 11:37:22', NULL, 0, 1);
INSERT INTO `brake` VALUES ('989170c529a348b3b93bf2a4653e8ea9', 'ct2', '入场口', 'admin', '2019-12-27 11:37:45', NULL, '2019-12-27 11:37:45', NULL, 0, 1);
INSERT INTO `brake` VALUES ('e489029055654bccb3cd601f0be71c41', 'ct3', '出场口', 'admin', '2019-12-27 11:37:36', NULL, '2019-12-27 11:37:36', NULL, 0, 1);
INSERT INTO `brake` VALUES ('f726873ed17441ea8dfbf78381bcde78', 'ct4', '出场口', 'admin', '2019-12-27 11:37:41', NULL, '2019-12-27 11:37:41', NULL, 0, 1);

车位数据

INSERT INTO `stall` VALUES ('004ac347b94e42bb8f0f6febd3265e35', 'P336', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);
INSERT INTO `stall` VALUES ('008773e089664ce49607c86b89dd8c0f', 'P250', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);
INSERT INTO `stall` VALUES ('0110ef02554f46ce91a3eeec6ecf2f95', 'P224', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);
INSERT INTO `stall` VALUES ('014f1f2b972e4e0092d749a7437f824d', 'P577', 0, 'admin', '2019-12-27 11:42:04', NULL, '2019-12-27 11:42:04', NULL, 0, 1);
INSERT INTO `stall` VALUES ('019f4aa0c22849e1a5758aaa33b855df', 'P229', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);

计费规则

INSERT INTO `charging_rule` VALUES ('41ed927623cf4a0bb5354b10100da992', 0, 30, 0, 'admin', '2019-12-27 11:26:08', NULL, '2019-12-27 11:26:08', '30 分钟内免费', 0, 1);
INSERT INTO `charging_rule` VALUES ('41ed927623cf4a0bb5354b10100da993', 31, 120, 5, 'admin', '2019-12-27 11:26:12', NULL, '2019-12-27 11:26:12', '2 小时内,5 元', 0, 1);
INSERT INTO `charging_rule` VALUES ('4edb0820241041e5a0f08d01992de4c0', 121, 720, 10, 'admin', '2019-12-27 11:34:06', NULL, '2019-12-27 11:34:06', '2 小时以上 12 小时以内,10 元', 0, 1);
INSERT INTO `charging_rule` VALUES ('7616fb412e824dcda41ed9367feadfda', 721, 1440, 20, 'admin', '2019-12-27 13:35:37', NULL, '2019-12-27 13:35:37', '12 时至 24 时,20 元', 0, 1);

非结构化存储

主要使用 Redis 中间件来存储可用车位的实时性信息,计费规则信息等热数据。

架构设计

没有最优的架构,只有最合适的架构,一切系统设计原则都要以解决业务问题为最终目标,并随着业务的发展,不断进行迭代演进。经过上面业务模块、存储模型的划分,基本的代码架构已经清晰可见。综合业务模块、微服务架构特性,输出功能架构设计图。

基于总体功能架构图,使用特定的功能组件即可实现相应的功能。前期也提到,Spring Cloud 全家桶中囊括了很多组件,开箱即用,这对快速上手微服务开发提供了极大的便利。同时,再融入时常开发实践一些常用的高效工具来提升编码效率如 Lombok,MBG 等。

留个思考题

组件库有提成 Lombok,对简化代码开发很有帮助。你有什么好用的组件库,能够在项目开发中,高效发挥作用呢?

具象业务需求再抽象分解——系统设计相关推荐

  1. 业务逻辑中的测试总结(二)----业务与数据库交互需求的测试分解

    对服务器端的测试,免不了对服务器端的接口与数据库之间进行各种交互的业务测试.这类隐藏类需求的分解实际上也能使测试人员站在一个更高的角度去分解实际的业务需求.对数据库的操作,一般都是增删查改这四类,所以 ...

  2. 从业务需求抽象成模型解决方案

    从业务需求调研,通过抽象转换成模型技术方案,本文将对这个过程做个拆解,供大家参考.以下我所说的可能都是错的,只是一家之见,欢迎大家在留言区多提意见和看法,互相共勉. 一.订单对象-信息需求 公司的运营 ...

  3. TOGAF:从业务架构到业务需求

    本文内容更新版本已转至  http://www.zhoujingen.cn/blog/3695.html -------------------------- 做管理型软件产品一般都要经历架构阶段,而 ...

  4. 我眼中BA(业务需求分析师)的技能广度和深度

    BA,或者称业务分析师,是企业数字能力和业务能力之间的沟通桥梁.随着企业数字转型的进一步深化,相信对BA这样的技能需求会越来越多,只是未必都用"BA/业务分析师"这样的Title. ...

  5. 业务需求复杂多变,IT部门应接不暇?自定义报表来帮您!

    从纸带打孔.到汇编语言.到高级语言,再到各种IDE.各种框架,人们始终在试图屏蔽底层的复杂性与难以理解性,通过归纳.抽象.封装,进而通过点拉拖拽及少量代码来快速完成应用程序的开发. 观远数据产品团队始 ...

  6. 业务需求、客户需求与功能需求

    首先有用户需求,然后由组织将用户需求转化为业务需求,再由开发者将业务需求转化为功能需求,功能需求映射到系统功能模块. 业务需求( business requirements):业务需求是公司基于收集的 ...

  7. 软件需求包括3个不同的层次 - 业务需求、用户需求和功能需求

    首先有用户需求,然后由组织将用户需求转化为业务需求,再由开发者将业务需求转化为功能需求,功能需求映射到系统功能模块.业务需求也有可能是基于的业务发展需要,由组织首先提出来的. 业务需求(Busines ...

  8. WebRTC:应用中最大难点在于根据业务需求的适当折中

    WebRTC对主流PC浏览器.移动端的全覆盖,对于开发者而言无疑是一剂强心针,而在去年W3C大会上又提出通过QUIC来实现WebRTC.但在实际应用.行业契合以及对H.265的支持依然存在着不可忽视的 ...

  9. 基于EasyNVR二次开发实现业务需求:用户、权限、设备管理

    许多接触到EasyNVR的用户.开发者都会提出关于EasyNVR设备分组和账户设备关系映射的问题,我们参考目前大部分的视频能力输出平台的做法,EasyNVR目前只做了唯一的用户/密码(类比appkey ...

最新文章

  1. vue 删除页面缓存_vue项目强制清除页面缓存的例子
  2. C++_代码重用3-私有继承
  3. Mysql 内置函数
  4. 跨进程访问(AIDL服务)
  5. 2011---2013年杭电计算机历年研究生复试---笔试编程
  6. 4-希尔排序C实现(递增递减的简单转换)
  7. 安装SQL2K,当创建挂起文件操作之后...
  8. 超详细前端开发案例:品优购商场项目(一)
  9. 如何处理“转换数据类型错误”错误?
  10. 2021年高考文科成绩 查询,预计2021年高考文科分数线
  11. python中使用frame需要安装_python – 在SFrame中分组而不安装graphlab
  12. 【自然语言处理】【聚类】ECIC:通过迭代分类增强短文本聚类
  13. 上海巨人网络面试经历
  14. 对话90后,移动互联网新生代力量行为调查-20140219早读课
  15. STM32中挂载SDRAM内存说明
  16. ue4 vr连接_基于UE4的VR项目基础环境配置和Motion Controller控制配置
  17. Python批量获取VOC测试集的类别
  18. Google Chrome 试用感受
  19. 23种设计模式——建造者模式
  20. 19级赵同学推荐的十佳医疗图像处理论文

热门文章

  1. nginx发布vue多页面程序
  2. 微软抠图软件Lazy Snapping背后的故事 - 网易学院·教程
  3. Wine - Windows模拟器
  4. graphpadY轴设置刻度不均匀_flotherm学习心得(参数设置)
  5. VML,The Vector Markup Language(矢量可标记语言)
  6. Windows server DNS服务器配置与管理
  7. 第四届红帽杯网络安全大赛 Web 部分writeup
  8. 详细讲解新闻发布会媒体邀约流程
  9. oracle的explain使用
  10. Qt实用技巧:QLineEdit限制只能输入Ip地址,且一直显示ip地址分段的“.”