框架分层规范

本规范参考大量业内优秀的架构分层思想(特别是好未来 Fend 框架,在此特别感谢),结合 MoChat 项目实际情况而制定,强烈建议大家阅读相关文章,具体参考文章见文末。

PHP行业常见框架分层为基础MVC,但是对于企业级应用且研发团队超过10人以上时并不适用,为了规范分层概念及研发标准特此在此文档进行分层规范详细定义。

此类规范并无绝对标准,重在统一规范,分层过多会带来一定维护成本和代码。

层级隔离性建议

  • 同层代码切勿相互引用调用,如出现此情况,建议将相互引用模块中某个模块降级到下一层,此举可提高业务代码隔离性
  • 上层服务可调用下层服务,不可下层服务反向调用上层服务,简化降低业务代码复杂度

通过以上设计,可以大量减少引用层级,方便未来业务管理拆分,代码纵向切面清晰完整。

关于最大分层

最大分层是指当业务复杂到一定程度,极限情况下的分层设计。

如业务简单则可对如下分层进行合并无需完全实现,具体可根据业务情况自行划分。

具体分层如下:

  • Action - 动作层(可理解为控制器)
  • Logic - 逻辑层(可选)
  • ServiceInterface - 服务接口层
  • Service - 服务层
  • Model - 模型层

分层的好处在于每一层完全隔离下一层所有细节接口规范统一,方便调用,对外提供服务规范文档,对内提供服务方便简单。

下面将会对具体分层功能职责、功能、规范进行详细介绍

注意:特殊情况特殊分析,欢迎找我们进行讨论进一步分析。


Action - 动作层

  • 对外业务封装层,所有对外接口页面封装必须都在此层,其他层级代码外部请求皆不可访问
  • 本层支持多级目录,默认请求路径与 Action 目录层级一致,如 /User/Login 默认请求路径为www.xxx.com/user/login
  • 外部请求常见在 url path 中包含参数,本框架也支持此方式但此举不推荐,主要原因对未来数据监控整理会带来大量工作
  • Action 层代码禁止调用 Action 层代码,限制过度引用可大量降低后续维护成本,如有此类业务需求,请将被调用代码降级到下一层内引用
  • 禁止在此层直接调用 Service 层及以下模块,如 Model、ORM配置层
  • 本层只调用 Service 进行业务拼装,调用 Service 不建议直接new Service类,请使用 ServiceInterface 对 Service 进行调用
  • 所有用户请求参数必须做安全过滤验证,相当的参数验证规则和错误提示信息都直接写在同 Action 文件

ServiceInterface

  • Action 调用 Service 服务时,必须统一使用此方式调用,这样 Action 层与 Service 层没有直接关联关系,并且能够统一规范 Service 所有行为,后续如果出现特殊情况可以在此层加入Hack代码临时解决问题,换取后续有充足时间进行无痛无感优化。
  • 通过此层封装 Action 层和后端服务完全隔离、若需要RPC化直接改此层内部实现代码即可实现前后端分离,如需对 Service 调用情况进行统计也可在此层增加统计分析代码等操作。
  • 此层可随时接入服务熔断,降级,cache,根据需要进行拦截管理

Service

  • Service 服务层内是具体业务逻辑,可使用 Model 对业务进行组装,对输入验证,输出格式都有统一的类进行封装管理;
  • 本层支持多级目录,具体可根据业务需要进行多层级封装,不推荐超过两层。如\v1(版本,可选)\User(类)\Login(函数);
  • 建议封装成可灵活组装业务逻辑的粒度供 Action 层组合,若牵扯事物建议最好在一个 Service 封装完成;
  • Service 层后续升级必须考虑兼容性,除非十分确认使用方会跟随升级;
  • 底层及本层所有逻辑异常或底层异常会抛出 Exception,此类 Exception 皆由 ServiceInterface 进行拦截,非必要、不要拦截;
  • 异常分两种:业务异常请使用正整数(用户未登录),底层异常及框架异常请使用负数(Mysql connect error,redis went away)作为错误码,通过这个简单定义可以让客户端研发人员更好区分错误类型;
  • 可复用性建议,同一个模块或服务数据实体仅存一份文件不要出现同一个数据实体分布在多个目录内情况,如 User 用户相关操作最终对外提供Service服务时必须所有定义在一个 Service 主体内,如:用户登陆有同样三种封装在不同文件内;
  • 同层 Service 代码不允许调用或引用同层 Service,如果出现建议将一些逻辑降级到 Model 层内;
  • 请保证每一个 Service 都是无状态的服务,不存在私有变量、static变量(factory变量除外)。

Model

  • Model层有很多种数据来源,目前已知 Redis,Mysql,其他服务提供的API,如果有多种来源有两种组织方式。 根据数据来源分目录后再根据对象名称分子目录或文件如/Model/Redis/User/User.php,不同来源不同目录 根据model层内代码汇总所有操作如/Model/User/User.php(内含,API,Redis,MySQL所有操作)
  • Model层对所有数据具体操作进行隔离,对外提供简单调用即可实现对数据的增删改查操作,此类封装只做基础封装不做具体业务封装,并不能保证业务事务完整性,如有此类联动多数据源操作一应封装在 Service 层。
  • 其中外层应保证传递数据做一些基础的安全过滤,Model为了效率没有对传递参数有效性做过多验证检测
  • Model 异常常见有两种,操作失败(Redis,Mysql,API请求失败,断开连接,事务失败),传递参数错误,此类都会抛出 Exception
  • 关于Cache,Model层的Cache只能保存常见的单条数据以及一些不太复杂的查询结果,如果对数据实时性较高不建议进行 Cache,如来源是第三方提供的数据源(Mysql,Redis,API)是无法保证实时同步更新 Cache 的,此类情况可另外做订阅更新,此类问题已经超出框架范畴。

ORM

  • 一般来说此层是隐含的,极少出现涉及本层单独定义情况,如果业务使用大量Redis key且需要统一定义管理才会使用本层。

规范参考文章