• 微服务系统设计——数据模型与系统架构设计


    摘要

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

    一、数据实体设计

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

    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 +版本。(这里需要的根据具体的数据存储类型选择合适的数据存储工具,是否需要的事务,查询效率……等。)结构化存储中建议设计一些通用字段,主要用于跟踪数据记录,库表结构通用字段如下:

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

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

    1. CREATE DATABASE `park_member` CHARACTER SET utf8 COLLATE utf8_general_ci;
    2. CREATE DATABASE `park_resource` CHARACTER SET utf8 COLLATE utf8_general_ci;
    3. CREATE DATABASE `park_charging` CHARACTER SET utf8 COLLATE utf8_general_ci;
    4. CREATE DATABASE `park_card` CHARACTER SET utf8 COLLATE utf8_general_ci;
    5. CREATE DATABASE `park_finance` CHARACTER SET utf8 COLLATE utf8_general_ci;
    6. CREATE DATABASE `park_message` CHARACTER SET utf8 COLLATE utf8_general_ci;
    7. CREATE DATABASE `park_carwash` CHARACTER SET utf8 COLLATE utf8_general_ci;

    3.1 park_member 库初始化表结构

    1. -- ----------------------------
    2. -- Table structure for member
    3. -- ----------------------------
    4. DROP TABLE IF EXISTS `member`;
    5. CREATE TABLE `member` (
    6. `id` varchar(32) NOT NULL DEFAULT '',
    7. `phone` varchar(11) DEFAULT NULL COMMENT '手机号',
    8. `birth` varchar(10) DEFAULT NULL COMMENT '生日',
    9. `full_name` varchar(20) DEFAULT NULL COMMENT '姓名',
    10. PRIMARY KEY (`id`) USING BTREE
    11. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员信息';
    12. -- ----------------------------
    13. -- Table structure for month_card
    14. -- ----------------------------
    15. DROP TABLE IF EXISTS `month_card`;
    16. CREATE TABLE `month_card` (
    17. `id` varchar(32) NOT NULL DEFAULT '',
    18. `card_no` varchar(20) DEFAULT NULL COMMENT '会员卡号',
    19. `start` varchar(16) DEFAULT NULL COMMENT '有效期起始',
    20. `ends` varchar(16) DEFAULT NULL COMMENT '有效期截止',
    21. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    22. PRIMARY KEY (`id`) USING BTREE
    23. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员月卡信息';
    24. -- ----------------------------
    25. -- Table structure for vehicle
    26. -- ----------------------------
    27. DROP TABLE IF EXISTS `vehicle`;
    28. CREATE TABLE `vehicle` (
    29. `id` varchar(32) NOT NULL DEFAULT '',
    30. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    31. `plate_no` varchar(10) DEFAULT NULL COMMENT '车牌号',
    32. `vehicle_inf` varchar(50) DEFAULT NULL COMMENT '车辆型号',
    33. PRIMARY KEY (`id`) USING BTREE
    34. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员车辆';

    3.2 park_resource 库初始化表结构

    1. -- ----------------------------
    2. -- Table structure for brake
    3. -- ----------------------------
    4. DROP TABLE IF EXISTS `brake`;
    5. CREATE TABLE `brake` (
    6. `id` varchar(32) NOT NULL DEFAULT '',
    7. `code` varchar(20) DEFAULT NULL COMMENT '编号',
    8. `desc` varchar(50) DEFAULT NULL COMMENT '描述',
    9. PRIMARY KEY (`id`) USING BTREE
    10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='停车场闸机';
    11. -- ----------------------------
    12. -- Table structure for stall
    13. -- ----------------------------
    14. DROP TABLE IF EXISTS `stall`;
    15. CREATE TABLE `stall` (
    16. `id` varchar(32) NOT NULL DEFAULT '',
    17. `code` varchar(10) DEFAULT NULL COMMENT '编号',
    18. `is_parked` int(2) DEFAULT NULL COMMENT '是否被占用',
    19. PRIMARY KEY (`id`) USING BTREE
    20. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车位表';
    21. -- ----------------------------
    22. -- Table structure for stall_parked
    23. -- ----------------------------
    24. DROP TABLE IF EXISTS `stall_parked`;
    25. CREATE TABLE `stall_parked` (
    26. `id` varchar(32) NOT NULL DEFAULT '',
    27. `stall_id` varchar(32) DEFAULT NULL COMMENT '车位编号',
    28. `plate_no` varchar(30) DEFAULT NULL COMMENT '车牌',
    29. `mtype` int(2) DEFAULT NULL COMMENT '0 入场,1 出场',
    30. PRIMARY KEY (`id`) USING BTREE
    31. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车位停靠记录';

    3.3 park_charging 库初始化表结构

    1. -- ----------------------------
    2. -- Table structure for charging_rule
    3. -- ----------------------------
    4. DROP TABLE IF EXISTS `charging_rule`;
    5. CREATE TABLE `charging_rule` (
    6. `id` varchar(32) NOT NULL DEFAULT '',
    7. `start` int(4) DEFAULT NULL COMMENT '停车时间起始',
    8. `end` int(4) DEFAULT NULL COMMENT '停车时间结束',
    9. `fee` float DEFAULT NULL COMMENT '收费',
    10. PRIMARY KEY (`id`) USING BTREE
    11. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='计费规则';
    12. -- ----------------------------
    13. -- Table structure for entrance
    14. -- ----------------------------
    15. DROP TABLE IF EXISTS `entrance`;
    16. CREATE TABLE `entrance` (
    17. `id` varchar(32) NOT NULL DEFAULT '',
    18. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    19. `plate_no` varchar(10) DEFAULT NULL COMMENT '车牌',
    20. `brake_id` varchar(32) DEFAULT NULL COMMENT '闸机号',
    21. PRIMARY KEY (`id`) USING BTREE,
    22. KEY `no_idx` (`plate_no`),
    23. KEY `member_idx` (`member_id`)
    24. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆入场';
    25. -- ----------------------------
    26. -- Table structure for vexists
    27. -- ----------------------------
    28. DROP TABLE IF EXISTS `vexists`;
    29. CREATE TABLE `vexists` (
    30. `id` varchar(32) NOT NULL DEFAULT '',
    31. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    32. `brake_id` varchar(32) DEFAULT NULL COMMENT '闸机号',
    33. `plate_no` varchar(32) DEFAULT NULL COMMENT '车牌号',
    34. PRIMARY KEY (`id`) USING BTREE,
    35. KEY `no_idx` (`plate_no`),
    36. KEY `member_idx` (`member_id`)
    37. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆驶出';

    3.4 park_card 库初始化表结构

    1. -- ----------------------------
    2. -- Table structure for exchange
    3. -- ----------------------------
    4. DROP TABLE IF EXISTS `exchange`;
    5. CREATE TABLE `exchange` (
    6. `id` varchar(32) NOT NULL DEFAULT '',
    7. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    8. `card_qty` int(11) DEFAULT NULL COMMENT '积分数量',
    9. `ctype` int(4) DEFAULT NULL COMMENT '0 优惠券,1 洗车券',
    10. `code` varchar(30) DEFAULT NULL COMMENT '券编码',
    11. PRIMARY KEY (`id`) USING BTREE
    12. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分兑换';
    13. -- ----------------------------
    14. -- Table structure for member_card
    15. -- ----------------------------
    16. DROP TABLE IF EXISTS `member_card`;
    17. CREATE TABLE `member_card` (
    18. `id` varchar(32) NOT NULL DEFAULT '',
    19. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    20. `cur_qty` varchar(20) DEFAULT NULL COMMENT '当前可用积分',
    21. PRIMARY KEY (`id`) USING BTREE
    22. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员积分';
    23. -- ----------------------------
    24. -- Table structure for member_sign
    25. -- ----------------------------
    26. DROP TABLE IF EXISTS `member_sign`;
    27. CREATE TABLE `member_sign` (
    28. `id` varchar(32) NOT NULL DEFAULT '',
    29. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    30. `cnt` int(4) DEFAULT NULL COMMENT '积分数量',
    31. `ctype` int(4) DEFAULT NULL COMMENT '0 签到,1 商场消费',
    32. PRIMARY KEY (`id`) USING BTREE
    33. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员签到';

    3.5 park_finance 库初始化表结构

    1. -- ----------------------------
    2. -- Table structure for billing
    3. -- ----------------------------
    4. DROP TABLE IF EXISTS `billing`;
    5. CREATE TABLE `billing` (
    6. `id` varchar(32) NOT NULL DEFAULT '',
    7. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    8. `fee` float DEFAULT '0' COMMENT '支付金额',
    9. `plate_no` varchar(10) DEFAULT NULL COMMENT '车牌号',
    10. `duration` float DEFAULT '0' COMMENT '停车时间',
    11. PRIMARY KEY (`id`) USING BTREE,
    12. KEY `no_idx` (`plate_no`)
    13. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆驶出支付流水';
    14. -- ----------------------------
    15. -- Table structure for month_card_recharge
    16. -- ----------------------------
    17. DROP TABLE IF EXISTS `month_card_recharge`;
    18. CREATE TABLE `month_card_recharge` (
    19. `id` varchar(32) NOT NULL DEFAULT '',
    20. `card_no` varchar(20) DEFAULT NULL COMMENT '月卡号',
    21. `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',
    22. `amount` float DEFAULT NULL COMMENT '充值金额',
    23. PRIMARY KEY (`id`) USING BTREE
    24. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员月卡充值';

    3.6 park_message 库初始化表结构

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

    3.7 park_carwash 库表结构

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

    3.8 初始化测试数据

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

    3.8.1 闸机数据:

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

    3.8.2 车位数据:

    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);
    2. INSERT INTO `stall` VALUES ('008773e089664ce49607c86b89dd8c0f', 'P250', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);
    3. INSERT INTO `stall` VALUES ('0110ef02554f46ce91a3eeec6ecf2f95', 'P224', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);
    4. INSERT INTO `stall` VALUES ('014f1f2b972e4e0092d749a7437f824d', 'P577', 0, 'admin', '2019-12-27 11:42:04', NULL, '2019-12-27 11:42:04', NULL, 0, 1);
    5. INSERT INTO `stall` VALUES ('019f4aa0c22849e1a5758aaa33b855df', 'P229', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);

    3.8.3 计费规则:

    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);
    2. 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);
    3. 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);
    4. 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);

    3.8.4 非结构化存储

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

    四、系统架构设计

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

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

    博文参考

  • 相关阅读:
    【优化组合】基于matlab人工蜂群算法求解投资优化组合问题【含Matlab源码 2137期】
    MyBatis在注解中使用动态查询
    【机器学习算法】神经网络与深度学习-8 1.1.1 CNN卷积神经网络(Convolutional neural Networks )详解
    PEO-PPO-SS-DTX聚合物胶束|PEG-PEI-PCL-SS-PLL|聚己内酯-紫杉醇高分子前药PTX-PCL(科研试剂)
    [前端]Preprocessor dependency "less" not found.
    零零信安:暗网分析报告——Part 5 他们自称无政府主义者
    矩阵按键简单使用
    我们又组织了一次欧洲最大开源社区活动,Hugging Face 博客欢迎社区成员发帖、Hugging Chat 功能更新!...
    JDK线程池ThreadPoolExecutor源码总结
    插件化原理
  • 原文地址:https://blog.csdn.net/weixin_41605937/article/details/125427694