目前项目开发中, 后端主要使用Spring Data Rest, 前端尝试过基于Vue和Bulma的单页模板vue-admin, 由于Bulma相对比较新, 成熟的组件库少. 后来引入了开源模板renren-security, 包含了用户, 组织和权限管理的基本功能, 代码结构较为简单, 前端使用了Vue, 也保留了传统组件, 便于模块间解耦和兼容旧页面. 为便于开发, 把前端改为前后端分离的多页应用, 参考了renren-fastplus和jeefast, 原项目前端与后端耦合并不多, 改动之处主要是使用了token认证替换了session认证, 并且把权限字符串发送到前端判断.(因此在后端需要认证权限, 否则可能会有伪造的请求) 另外使用了通用组件处理CRUD的公共功能d2-crud. 该组件最初取自D2项目团队D2 Projects, 开始关注该项目是由于在飞冰上看到他们发布的Vue项目脚手架, 有一套相关的项目, 形成了自己的小生态. 我一直认为有潜力的项目会重视生态建设, 但是他们的整体项目仍是单页模板, 难于和旧代码集成, 所以只用了CRUD组件. 当时该组件还处于开发初期, 代码较简单, 但作者是个人开发, 需要的一些功能等不及, 所以自己做了表格和表单组件的拆分, 引入了spring-data-rest-js与后端通信, 使用了AlaSQL在前端进行数据缓存和导入导出. 对于数据表之间的关联也在前端实现.
这一套工具组合起来, 简单的增删改查页面开发效率还可以, 基本不需要写代码, 每个模块只需要后端写好实体类, 前端建立数据配置, 建好相应的页面, 配好菜单路径和权限, 就可以使用了. 特定的需求可以在前端添加按钮和函数, 原则上不需要在后端修改.
目前感觉到开发中存在的问题:
Spring Data Rest功能较弱, 不能方便的实现复杂查询. 目前是通过把数据一次全部加载到前端, 在前端进行查询处理. 后期还是要考虑通过后端接口查询, 需要引入新的实现方式.
前端组件标准化不够. 通过不同场景下的使用需求可以提炼出一些前端通用的需求, 为了避免代码重复做了一些组件封装, 但是只是粗略实现了基本功能, 不够清晰简洁.
需要手工对接的地方还是较多, 需要按照前端和后端的技术需要去适配同一个业务需求. 未来需要整理业务中通用的需求, 模式, 形成通用的声明化配置化的代码基础, 方便后续的开发.
所以接下来, 准备使用PostgreSql数据库替换Mysql, 经调查测试, 其在性能和功能方面都更优秀. 然后可以使用PostgREST提供Http接口, 避免了手工维护后端接口, 前端在权限范围内可按需操作后端数据. 权限判断等必须在后端完成的功能通过在数据库中声明, 简化了开发, 也保证了数据的完整性. 关于这种后端自动生成接口的技术, 其实一直在关注. 原则上后端的很多工作就是把数据库的访问转成HTTP的接口, 这种工作应该自动完成更好. 如果数据库没法直接提供HTTP形式的接口(比如像CouchDB那样), 那么最好是只通过一层简单的封装, 对外暴露Web接口. 这方面目前感觉做的最成熟的就是PostgREST. 以前一直使用的是Spring Data Rest, 只提供了最简单的CRUD和分页功能, 并且需要Spring环境. 在后端只提供数据接口的情况下, 显得结构复杂, 功能单薄. github上有人建了一个列表automatic-api, 总体来看这方面的技术多数是小规模的探索, 没有形成整体的开发架构. 最近看到国内也有人在做这种尝试APIJSON. 这种思想和实现并不是很难, 和所有的ORM或数据访问框架一样, 都是拼SQL语句. 关键是要形成完整的开发模型, 需要一套相关项目和工具组成的生态系统. 其中首先要解决的是权限控制问题. 很多人说, 前端直接传SQL语句到后端执行最简单了, 但是不解决授权验证的问题, 就只能作为原型技术来用. PostgREST利用了PostgreSql数据库本身强大的权限管理和编程功能, 利用JWT传递权限角色到数据库, 所有的授权判断直接利用数据库本身的管理功能. 最大化的减少了代码的冗余, 也保证了用于生产环境的规范化. 原则上, 只要是权限范围内的操作都可以在前端执行, 后端不需要写业务相关代码. 当然, 如果需要写的话, 可以在数据库中写存储过程(函数), 前端通过Web接口调用. 这样的技术架构实际上把项目整体简化成了最为本质的两层: 数据 和 界面. 这是软件的本质功能, 其余所有的环节都属于中间步骤, 可以被简化, 规范化和通用化. 这是最初和最终的目标, 参见第一篇文章.
至于这种模式存在的问题, 首先是最好把数据协议和传输协议分开, 虽然现在HTTP是Web标准, 但是面向未来, 最好不要捆绑在特定的传输协议上, 只做好Web可用的数据访问接口, 底层使用websocket, 或者HTTP2, HTTP3, 或者别的什么协议都可以. 这个问题其实是由于TCP/IP协议简化的应用层造成的, OSI模型中的会话层和表示层是有必要的, 而且并不需要特殊硬件, 应该是在软件中分层. 把所有功能都简化到应用层的后果就是HTTP承担了太多角色, 没有一个清晰的层次, 导致了Web开发需要后端写很多难以复用的接口. 所以REST和GraphQL都在填这个坑, 我觉得最根本的方式还是重视OSI模型, 在软件架构中分离出会话和表示层, 建立起通用规范的实现和接口调用方式, 这样不仅对于Web, 对于更底层直接基于socket的调用都是大有好处的, 现在的情况下是这些功能分布于通讯框架和业务代码中, 没有一个清晰的结构, 增加了复杂性. 当然, 这是从长远和理想角度考虑, 目前HTTP接口还是够用的, 前端通过对应的客户端库转接相应格式的接口, 后续切换应该也问题不大.
其次是绑定了特定数据库的问题, 这个问题目前看来并没有太好的解决方式. 数据库领域的分裂割据跟操作系统, 浏览器, 编程语言一样, 由来已久, 短期难以消除. 如果我们想只用标准SQL, 使用各个数据库通用的部分, 那么最好能像在前端一样, 用jQuery这样的库处理浏览器差异, 但是目前看来没有哪个项目能承担起这个重任. 从中小企业的需求和现状来看, 大部分使用数据库只是简单的数据存储, MySQL也能满足要求. 但是MySQL也有很多历史遗留的和现实的问题, 功能偏弱, 不能统一. 目前看分裂有可能越来越大, 官方的MySQL8加入了角色管理, 但是社区很多已经转向了MariaDB, 未来发展方向并不明朗. 而PostgreSql虽然显得相对低调, 近几年却也呈现一路上升之势. 加上一大批相关项目的支持, 未来成为数据库领域的Chrome也未可知. 毕竟, 如果开源软件功能足够强大, 生态足够丰富, 大部分企业也不会错过压缩成本的好机会.
另外, 关于数据库内置的权限系统能不能很好的满足业务特定的需求, 需要实践验证. 个人感觉确实是存在强耦合的风险. 目前的想法是, 让业务角色和数据库角色关联, 发挥合力.
前端方面, 通用组件还需要继续优化整理, 保证通用功能不出现重复代码, 特定功能可以用简洁的声明式代码实现. 在此基础上加强前后端的整合, 屏蔽技术实现细节带来的差异, 提炼出业务常用类型和流程, 比如人员, 机构, 日期时间, 货币, 任务, 单据. 更多的建立起基于业务的抽象, 业务类型决定了数据库类型和前端组件类型. 对于简单通用的需求, 只要声明一次, 前后端都能够以统一的方式成功运行. 对于特定的需求, 能够在实际需要的地方(一般原则是权限范围内的在前端, 需要控制权限的在后端)插入声明代码, 即可运行. 并且争取做到用户可以简单的理解和配置, 在无需改动代码和开发人员不参与的情况下实现系统按需修改.
最终的目标是希望真正能实现业务和技术的解耦. 业务规则应该是数据化, 声明化的. 框架解析业务规则, 并产生能够在计算机上运行的程序. 业务的变化不需要修改技术实现代码, 技术的迭代也不会影响业务的正确运行. 这是软件开发的长远目标, 也是降低软件复杂性和开发维护成本的根本方式. 结合当前的技术情况, 具体形式采用DSL, 还是SQL, XML或LISP, 或者干脆用规则表, 现在不能确定, 但总的思路是业务是数据, 就像函数是数据一样. 声明式的业务规则在底层框架的支持下自动转换成程序在计算机上运行. 技术和业务的复杂性相互独立. 开发人员的主要精力应该是理解业务需求, 设计合适的数据库和界面, 并以声明的方式直白的翻译业务需求. 开发的难度大大降低, 无经验的人员, 甚至客户企业工作人员简单熟悉后都可以参与开发, 把软件的控制权交给用户. 而专业技术人员更多的是负责保证底层环境的稳定运行, 实际上就是现在的云模式.
希望在不远的将来, 码农, 996, 软件培训这些概念会像旧时的黄包车, 西洋景, 大哥大, BP机一样, 成为一种模糊的回忆. 生活在一个改变的年代, 我们都在努力奔跑的同时, 也享受着别人的努力所带来的便捷.