Skip to main content

One post tagged with "BFF"

View All Tags

· 8 min read
石晓波

原则基本概念

SOLID

程序设计领域, SOLID (单⼀一功能、开闭原则、⾥里里⽒氏替换、接⼝口隔离以及依赖反转)是由罗伯特·C·⻢马丁在21世纪早期 引⼊入的记忆术⾸首字⺟母缩略略字,指代了了⾯面向对象编程和⾯面向对象设计的五个基本原则。当这些原则被⼀一起应⽤用时,它们使得⼀一个程序员开发⼀一个容易易进⾏行行软件维护和扩展的系统变得更更加可能SOLID被典型的应⽤用在测试驱动开发上,并且是敏敏捷开发以及⾃自适应软件开发的基本原则的重要组成部分。

IOC

依赖倒置原则(Dependency Inversion Principle,DIP)规定:代码应当取决于抽象概念,⽽而不不是具体实现。⾼高层模块不不应该依赖于低层模块,⼆二者都应该依赖于抽象抽象不不应该依赖于细节,细节应该依赖于抽象 (总结解耦)类可能依赖于其他类来执⾏行行其⼯工作。但是,它们不不应当依赖于该类的特定具体实现,⽽而应当是它的抽象。这个原则实在是太重要了了,社会的分⼯工化,标准化都 是这个设计原则的体现。显然,这⼀一概念会⼤大⼤大提⾼高系统的灵活性。如果类只关⼼心它们⽤用于⽀支持特定契约⽽而不不是特定类型的组件,就可以快速⽽而轻松地修改这些低级 服务的功能,同时最⼤大限度地降低对系统其余部分的影响。

AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为:⾯面向切⾯面编程,通过预编译⽅方式和运⾏行行期动态代理理实现程序功能的统⼀一维护的⼀一种技 术。AOP是OOP的延续,是软件开发中的⼀一个热点,也是Spring框架中的⼀一个重要内容,是函数式 编程的⼀一种衍⽣生范型。利利⽤用AOP可以对业务逻辑的各个部分进⾏行行隔离,从⽽而使得业务逻辑各部分之间的耦合度降低,提⾼高程序的可重⽤用性,同时提⾼高了了开发的效率

编码实现

model

定义数据类型以User为例,定义User

/src/models/user.ts
export namespace Models {
export class User {
email: string;
name:string;
}
}

interface

定义接口,service需要实现接口,代码如下

/src/interface/IIndex.ts
import { Models } from "../models/User";

export interface IIndex {
getUser(id:number): Models.User;
}

service

已经定义了数据类型和接口,接下来完成service类,implements 接口。使用装饰器的方式自动将service绑定到container中。同时使用symbol为每个service定义唯一标识符,完成如上代码只是完成了对service的标记,代表当前service可以注入到container中。如果要在controller中注入service查看接下来的步骤。

src/services/indexService.ts
import { IIndex } from "../interface/IIndex";
import { Models } from "../models/User";
import { provide, buildProviderModule } from "inversify-binding-decorators";
import { TAGS } from "../constant/tags";

@provide(TAGS.IndexService)
export class IndexService implements IIndex {
constructor() {}
private userStorage: Models.User[] = [
{
email: "123123@qq.com",
name: "小名",
},
{
email: "12312312@qq.com",
name: "小同",
},
];
getUser(id: number) {
let result: Models.User;
result = this.userStorage[id];
return result;
}
}

controller

定义controller,controller中使用inversify库提供的inject装饰器注入要使用的service,使用inversify-koa-utils库提供的controller装饰器绑定路由和controller的关联关系。controller中的action使用httpGet装饰器绑定具体的路由和响应操作。这里需要注意一点controller需要按需加载,只有路由匹配了才能加载对应的controller而不能像service一样一次性加入到container中,这里就需要使用inversify-binding-decorators提供的fluentProvide流式provider。代码如下:

src/controller/indexController.ts
import { inject, injectable } from "inversify";
import { interfaces, controller, httpGet,TYPE} from "inversify-koa-utils";
import { TAGS } from "../constant/tags";
import { IIndex } from "../interface/IIndex";
import {IRouterContext} from 'koa-router';
import { provideThrowable } from "../ioc";
// import {BaseContext} from 'koa';
//service一次性加载到容器中,controller需要在遇到这个路由的时候才会加载对应的controller 流式的provider
@provideThrowable(TYPE.Controller,'IndexController')
@controller('/')
export default class IndexController implements interfaces.Controller {
private indexService: IIndex;
constructor(@inject(TAGS.IndexService) indexService) {
this.indexService = indexService;
}

@httpGet('/')
private async indexAction(ctx:IRouterContext, next:Promise<unknown>):Promise<any> {
const data = this.indexService.getUser(1);
ctx.body = {
data,
}
}
}
src/ioc/index.ts
import { fluentProvide } from "inversify-binding-decorators"

//别名和名称
let provideThrowable = (identifier,name) => {
return fluentProvide(identifier).whenTargetNamed(name).done();
}

export {
provideThrowable
};

入口文件

通过如上编码已经实现了controller、service、model接下来需要完成入口文件编写,注意因为使用了元编程所以需要在入口文件的最顶部引入reflect-metadata,按需引入controller和service虽然定义了装饰器在类上但是没有手动引入需要使用的controller和service,所以可以定义loader.ts文件 手动引入所有要使用的controller和service文件然后在入口文件中引入loader即可。这些准备工作都完成后开始创建容器container,使用inversify提供的Container类创建实例即可。bind容器和service,可以通过手动的方式一个一个绑定也可以通过inversify-binding-decorators库提供的buildProviderModule方法自动绑定。使用inversify-koa-utils库提供的InversifyKoaServer类创建server。具体代码如下:

src/app.ts
import "reflect-metadata";
import "./ioc/loader";

import { InversifyKoaServer } from "inversify-koa-utils";
import { Container } from "inversify";
import { buildProviderModule } from "inversify-binding-decorators";

const container = new Container();
container.load(buildProviderModule());
const server = new InversifyKoaServer(container);

server
.setErrorConfig((app) => {
//错误处理中间件
})
.setConfig((app) => {
//其他中间件
});
const app = server.build();

app.listen(3001, () => {
console.log("inversify server 启动成功");
});

总结

如上只是完成了一个简单的基于SOLID的BFF架构代码,可以引入一些其他的中间件丰富工程,也可以配置webpack编译出可以在生产环境的代码。完整代码点击查看