api 微服务框架 php,[2.17]-微服务:Api接口服务层 | PhalApi(π框架) - PHP轻量级开源接口框架 - 接口,从简单开始!...

我发现,我越是努力,就越发幸运。 -- Thomas Jefferson

2.17.1 微服务

6734b6ce343b49a01efdc915014563f6.png

Martin Fowler(我喜欢和敬仰的大师)曾发表了上面这一段话。这段话也出现在了2015年QCon分享会上,并加了一张PPT“什么是微服务”加以说明。

15d648d0c2164794c53532c1c878f7a7.png

里面提到了 微服务 这个概念,在PhalApi框架中即对应我们的Api接口服务层,只是我们不是称之为微服务,而是接口服务。

不管何种说法,我们都应该关注里面提及到的这几点重要特质:

小,且专注于做一件事情

独立的进程中

轻量级的通信机制

松耦合、独立部署

这里不过多地讨论微服务相关的分享,而是重温接口服务层Api与领域驱动和单元测试之间的关系,以及如何开发一个优雅、稳定又简单的接口。

2.17.2 层级调用的顺序

整体上讲根据从Api接口层、Domain领域层再到Model数据源层的顺序进行开发。

在开发过程中,需要注意不能 越层调用 也不能 逆向调用 ,即不能Api调用Model。而应该是 上层调用下层,或者同层级调用 ,也就是说,我们应该:

Api层调用Domain层

Domain层调用Domain层

Domain层调用Model层

Model层调用Model层

如果用一张图来表示,则是:

ec0b9b82960fe9fca49b2c6ee5b4ae60.png

为了更明确调用的关系,以下调用是 错误 的:

错误的做法1:Api层直接调用Model层

错误的做法2: Domain层调用Api层,也不应用将Api层对象传递给Domain层

错误的做法3: Model层调用Domain层

这样的约定,便于我们形成统一的开发规范,降低学习维护成本。

比如需要添加缓存,我们知道应该定位到Model层数据源进行扩展;若发现业务规则处理不当,则应该进入Domain层探其究竟;如果需要对接口的参数进行调整,即使是新手也知道应该找到对应的Api文件进行改动。

2.17.3 接口服务的定义

现实项目开发过程中,绝大部分我们编写的接口是给别人使用的,或许给Android客户端同学使用,或者给iOS客户端同学使用,抑或提供给其他后台系统的同学使用。

为了提高并行开发的速度,我们不能等待接口完全开发完成后才提供接口文档,而且他们也不能忍受这么漫长的等待。

所以,客户端同学时常会问我们:什么时候可以提供接口文档?

我们提倡“接口先行”,如果有时不能很好地做到这一点(毕竟多变的需求促发多变的情境),我们可以快速提供接口的定义。

这有点像规约层对接口的定义一样,在PhalApi中定义一个接口,再具体一点即:

1、创建一个接口服务类和声明函数

2、配置接口参数规则

3、提供接口返回格式的注释

简单来说,就是创建一个类,写个函数,定义参数和返回结果。

(1)创建一个接口服务类和声明函数

//$ vim ./Vote/Api/Act.php

class Api_Act extends PhalApi_Api {

public function joinIn() {

}

}

(2)配置接口参数规则

class Api_Act extends PhalApi_Api {

public function getRules() {

return array(

'joinIn' => array(

'teamName' => array('name' => 'team_name', 'require' => true, 'min' => 1, 'max' => 100),

),

);

}

public function joinIn() {

}

}

(3)提供接口返回格式的注释

class Api_Act extends PhalApi_Api {

public function getRules() {

return array(

'joinIn' => array(

'teamName' => array('name' => 'team_name', 'require' => true, 'min' => 1, 'max' => 100),

),

);

}

/**

* 团队参赛接口

*

* @return int code 0,参赛成功;1,队名已存在

* @return int team_id 新建的团队ID

*/

public function joinIn() {

}

}

(4)在线查看一下效果

在完成上面的动作后,我们可以通过在线工具来看下实时的效果,在浏览打开后访问:

http://api.vote.phalapi.com/vote/checkApiParams.php?service=Act.JoinIn

可以看到:

e76d68c5db8142e047d94bcc2d7b2dc6.png

到了这里,即使我们未完成接口的开发,也未提供更完善的接口文档,但接口客户端同学也可以根据这个在线的接口参数进行开发了。

2.17.4 在ATDD下讲述故事

我们一直推崇测试驱动开发,但在对于Api接口开发,更准确来说是ATDD,即:验收测试驱动开发(Acceptance Test Driven Development)。

在前面Domain层文档中,我们提到了Api层是讲述故事的场景。因此,为了验证我们的业务场景是否正确,我们应该事先编写好单元测试,以不断引导我们前往正确的目的地。

我们可以使用脚本来快速生成测试骨架:

$ pwd

$ /path/to/api.vote.phalapi.com/Vote/Tests/Api

$ phalapi-buildtest ../../Api/Act.php Api_Act ../test_env.php > Api_Act_Test.php

然后,稍微修改完善测试场景:

/**

* @group testJoinIn

*/

public function testJoinIn()

{

//Step 1. 构建请求URL

$url = 'service=Act.JoinIn';

$params = array(

'sign' => 'phalapi',

'team_name' => 'test team name',

'user_id' => '1',

'token' => '193CE82D1F4588A9A168BDE6E6B83868B1464F523D16C05206F308E51EB91731',

);

DI()->notorm->team->where('team_name', $params['team_name'])->delete();

//Step 2. 执行请求

$rs = PhalApi_Helper_TestRunner::go($url, $params);

//var_dump($rs);

//Step 3. 验证

$this->assertNotEmpty($rs);

$this->assertArrayHasKey('code', $rs);

$this->assertArrayHasKey('team_id', $rs);

$this->assertEquals(0, $rs['code']);

$this->assertGreaterThan(0, $rs['team_id']);

//create again

$rs = PhalApi_Helper_TestRunner::go($url, $params);

$this->assertEquals(1, $rs['code']);

}

从上面测试的代码可以看出,我们先后进行了两次报名。明显地,第一次报名应该是成功的,第二次则应该提示不能重复报名。

单元测试的好处,不但在于可以引导我们做正确的事情,还可以提高我们的关注点,不致于在开发过程中被各种事务(如临时性的会议)打断后回来却不知刚才开发到哪了。

然而,更多的是为后期的维护、扩展提供可验证的业务场景。这点是很重要的。因为每一个测试场景,都保存了对应场景的模拟信息,这样不仅仅在后面的扩展,还是突如其来的BUGFIXED,我们都可以快速证明我们的修改是正确的,至少不会影响到原来的业务流程。

试想一下,如果原来可以下单支付的接口,突然被影响到而导致支付不成功,这是何等的损失!

我现在慢慢地,每当需要修改别人的代码时,我都会看下有没有对应的单元测试。如果没有,我会先补回,这样能增强我修改别人代码的信心。

2.17.5 精益接口开发

传统的接口开发,由于没有很好的分层结构,而且热衷于在一个文件里面完成绝大部分事情,最终导致了臃肿漫长的代码,也就是通常所说的意大利面条式的代码。

在PhalApi中,我们针对接口领域开发,提供了新的分层思想:ADM(Api - Domain - Model)。

即便这样,如果项目在实际开发中,仍然使用原来的做法,纵使使用再好的接口开发框架,也还是会退化到原来的局面。

为了能让大家更为明确Api接口层的职责所在,我们建议:

(1)Api接口层应该做:

应该:对用户登录态进行必要的检测

应该:控制业务场景的主流程,创建领域业务实例,并进行调用

应该:进行必要的日记纪录

应该:返回接口结果

(2)Api接口层不应该做:

不应该:进行业务规则的处理或者计算

不应该:关心数据是否使用缓存,或进行缓存相关的直接操作

不应该:直接操作数据库

不应该:将多个接口合并在一起

在明确了上面应该做的和不应该做的,并且也完成了接口的定义,还有验收测序驱动开发的场景准备后,相信这时,即使是新手也可以编写出高质量的接口代码。

因为他会受到约束,他知道他需要做什么,主要他按照限定的开发流程和约定稍加努力即可。

如果真的这样,相信我们也就慢慢能体会到 精益开发 的乐趣。

最后,让我们一起来看下上述团队参赛接口开发的代码实现:

/**

* 团队参赛接口

*

* @return int code 0,参赛成功;1,队名已存在

* @return int team_id 新建的团队ID

*/

public function joinIn() {

$rs = array('code' => 0, 'team_id' => 0);

DI()->userLite->check(true);

$domain = new Domain_Team();

if ($domain->isExists($this->teamName)) {

$rs['code'] = 1;

return $rs;

}

$teamId = $domain->joinIn($this->teamName);

$rs['team_id'] = $teamId;

return $rs;

}

可以看出,上面的代码短小达意,简单清晰。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值