Heim  >  Artikel  >  Backend-Entwicklung  >  基于yaf的模块化开发方案

基于yaf的模块化开发方案

WBOY
WBOYOriginal
2016-06-23 13:16:261056Durchsuche

随着我们网站流量越来越大,用户越来越多,需求也越来越复杂,一站式开发已经远远不能支撑我们的业务发展需要,在产品发展期内碰到的问题越来越多,技术架构限制了业务的发展,这对于研发人员来讲是不能忍的,业务制约技术发展可以接受,但技术制约了业务的发展就是问题了。

我先来总结下一站式开发将面临的问题:

1、代码量大,开发、部署、测试都将是问题

这个很容易理解,我们的业务需求全部在一个系统中的时候,开发、部署、测试必然都会存在很大的风险,比如开发,即使从一开始就参与项目的成员,在业务规模庞大的时候,也很难做把控控制风险;比如部署,因为量大,导致代码发布会非常慢,大部分发布平台会先打包,到线上服务器解压,这个过程当在紧急上线发布的时候会急死人的,一旦上线出现问题,回滚成本将会更高,影响的不单单是发布系统,严重的将导致服务不可用的状态。

2、严重制约业务发展

有一个好的习惯,开发人员会在开发过程中习惯性的将公用的功能封装成类或方法,如果这是核心功能,那它可能出现在整个系统的任何地方,当核心功能需要开发的时候,则会牵一发动全身,很可能面临的将是系统重构。

举个很简单的例子。

如我们有一个函数(或者一个类)

function custom_function($a,$b) {//TODO}

实现了最基础的业务功能,一开始它满足了当前的业务,实现了基本功能,但随着业务发展,这个基础功能变得更强大,支持的特性更多了,为了支持业务,我们把函数扩展根据参数做兼容,如:

function custom_function($a,$b,$c){if(empty($c)){//兼容老业务}else if($c== "XXX"){//支持新业务}else if($c == "ccc"){//新业务}}

虽然能满足需求,但可以想像维护这样一段代码会让后来人骂娘多少次?当然,虽然我们听不见,但毕竟让后人非常不爽。

3、不得已的推倒重构

基于各种压力,更多的是基于业务发展的压力,我们不得已痛苦的对整个系统进行重构,这个过程漫长而痛苦,一方面要支持现有系统的业务,另一方面还要对整个系统进行技术重构,让他们之间的耦合更少,这个成本和风险将会让我们痛不欲生。

那我们在项目最初是不是就该考虑从业务上拆分成多个模块?答案是肯定的。但是也将带来一些问题:

1、PM不懂技术,不清楚怎么拆分

这个问题还好解决,在规划和项目评审的时候,跟技术一起讨论,侧重于在业务功能上进行拆分,比如用户相关操作全部归为用户模块,后台相关归为后台模块,浏览的归为view模块

2、怎么控制粒度,控制不好反而会增加更多的成本

这个要在技术内部好好的做沟通和规划,控制好边界

3、数据共享?

虽然拆成了多个模块,但是对于数据俩讲,它遍布整个系统,模块与模块之间的数据如何共享?

在项目初期的解决方案是,

- 将最基础的数据独立成library模块,它提供最细单元的数据共享,这些可以在整个模块中调用;

- 提供跨模块调用的业务接口,比如在模块A下,让框架支持调用B模块下的接口;

- 提供http服务化接口,这个大多数是在项目成熟阶段的时候才会做的事情;

解决困难问题的方向就在框架身上了,它将具备以下特性:

1、代码量少、精简

2、可跨模块调用,模块中几乎不用包含任何框架的实现

3、可调用最基础的library模块

4、管理配置化

在目前所有php框架中,要想实现模块化编程还是比较困难的,大部分的框架实现都是在框架内部有自己的application或module,这里面是具体的模块化的业务代码。

还有一种完全的模块化,每一个模块业务都有这样一套框架实现:

但是这样的缺点显而易见,一方面很难做到跨模块调用,需要在外层在包一层逻辑实现,另一方面框架维护和代码量让模块显得更重。

所以更多的是采用第一种方式,今天的主题yaf,其实也是这样一种架构,只是框架逻辑实现在so扩展中了,

但今天所要谈的是基于这两种方案之中的办法,既能让模块之间完全独立,又能让它跨模块调用接口,而且模块中基本不用维护任何框架代码,只需要按照框架的规范写代码就可以了。

每个模块之间提供interface,供其他模块直接调用,每个模块只处理自己的业务,当有业务需要共享时直接按照规范写interface就可以了。

项目目录如下:

但是yaf不支持跨模块的调用,模块与模块之间是不能通过interface来通信的,为了解决这个问题,我增加了两个类Yaf_Init和Yaf_Caller

Yaf_Init

$app = Yaf_Init::init();$app->Bootstrap()->run();

这个只是一层封装,根据目录解析出当前模块名,省去配置config.ini的烦恼,为了灵活性,Yaf_init::init()后面将增加参数可以灵活自定义配置,每一个webroot/module/index.php中都是上面的代码。

等同于下面

$config = array('application'=> array('directory' => app目录))$application = new Yaf_Application($config);$application->Bootstrap()->run();

为了能够找到app的目录,还需要在php.ini中增加一个配置,

yaf.app_path = /home/admin/web/htdocs/app/

如果访问/home/admin/web/webserver/webroot/module/index.php时,

会解析出module拼成app目录:

/home/admin/web/htdocs/app/module/传给$config.application.directory,

将$config 配置传递给yaf_application的构造函数进行初始化

Yaf_Caller

$Object = new Yaf_Caller('moduleName','className');$Object->test();

这个类用来实例化其他模块中interface的类库,其实它也是一层简单的包装,保存环境变量,切换到某模块,然后调用Yaf_loader::autoload(),该构造函数接收两个参数,第一个是模块名,第二个是interface的类名,

interface的命名规范:

/app/htdocs/moduleA/Interface/Admin.php

比如模块A提供了一个interface,类名是admin

则Admin.php的类写法

class Interface_Admin{   function test(){        //TODO   }}

其他模块调用时

//object返回的是Interface_Admin类$object = new Yaf_Caller('moduleA','Admin');它直接可以调用Interface_Admin下的public方法。$object->test();

这套方案有以下优点:

- 模块代码量小,只需要实现业务代码就可以

- 模块之间可以无缝调用inteface接口

未完待续……

原文出处:

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn