说明
在本项目中,应用了CQRS架构模式,区别于普通的MVC+S(Service)模式,在这个项目中很少会使用到Service,而是通过定义一个指令,通过发布指令来完成某件事。
下面我们以创建一个查询商品列表的接口为例,带你走进如何在本框架中使用查询
创建Query
在application/query/product路径下,创建一个文件,文件命名为:interface.ts。
//src\application\query\product\interface.ts//定义一个查询商品列表的Query接口,包含productName(商品名称)、page(页数)、limit(每页显示数量)等内容export interface IListProductQuery extends IQueryBase {//名称前添加I表示这是一个接口,每个Query应当继承IQueryBase。productName: string;page: number;limit: number;}
接下来实现这个接口。在application/query/product/impl路径下,创建一个文件,文件命名为:list-product.query.ts,单词用横杠隔开。query是一个标志,用小数点隔开,突出这是一个定义Query的文件。后面会出一份命名规范文档。
//src\application\query\product\impl\list-product.query.ts
import { QueryBase } from '../../../../infrastructure/core/application/query';
import { IListProductQuery } from '../interface';
export class ListProductQuery extends QueryBase implements IListProductQuery {
productName: string;
page: number;
limit: number;
//最好为每个属性定义set方法,在各自的set方法中添加业务逻辑。
constructor(productName: string, page: number, limit: number) {
super();
this.setProductName(productName);
this.setPage(page);
this.setLimit(limit);
}
setProductName(productName: string): void {
this.productName = productName;
}
setPage(page: number): void {
if (page <= 0) {
throw new Error('页数不能小于等于0');
}
this.page = page;
}
setLimit(limit: number): void {
if (limit <= 0) {
throw new Error('limit不能小于等于0');
}
if (limit > 200) {
throw new Error('limit数值太大');
}
this.limit = limit;
}
}
创建QueryExecutor
上面我们创建了一个查询指令,接下来我们来创建执行者。
//src\application\query.executor\product\interface.ts
export interface IListProductExecutor extends IQueryExecutorBase {
dbContext: MongoDbContext;
}
//src\application\query.executor\product\impl\list-product.executor.ts
import { Inject, Provide } from '@midwayjs/decorator';
import {
QueryBase,
QueryExecutorBase,
} from '../../../../infrastructure/core/application/query';
import { MongoDbContext } from '../../../../infrastructure/db/mongodb/db-context';
import { SubscribeQuery } from '../../../../infrastructure/decorator/query';
import { ListProductQuery } from '../../../query/product/impl/list-product.query';
import { IListProductExecutor } from '../interface';
//订阅上面刚刚创建的Query指令
@SubscribeQuery(ListProductQuery)
@Provide() //添加到IoC容器 必填
export class ListProductExecutor
extends QueryExecutorBase
implements IListProductExecutor {
@Inject('mongoDbContext') //从IoC获取实例 必填
dbContext: MongoDbContext;
async executeQuery<Q extends QueryBase>(query: Q): Promise<any> {
if (query instanceof ListProductQuery) {
this.dbContext
const products = this.dbContext.list(
'product', //模型名称
{
//筛选条件
productName: new RegExp(query.productName),
},
null, //字段过滤
{
//执行参数
skip: (query.page - 1) * query.limit,
limit: query.limit,
}
);
return products;
}
throw new Error('未识别的指令');
}
}
在代码中,我们使用@Provide()和@SubscribeQuery对ListProductExecutor进行装饰,前者是将ListProductExecutor交给IoC容器托管,而后者则表示订阅某个指令,本框架会在项目启动的时候自动收集这些订阅,存储在查询总线中,在发送查询指令自动转发给查询指令执行者。
执行查询指令
在web/api下创建一个Controller。
//src\web\dto\product.ts
export class ListProductDTO {
productName: string;
page: number;
limit: number;
}
//src\web\api\product.ts
@Provide()
@Controller('/products')
export class ProductController {
@Inject()
queryBus: IQueryBus;//查询总线
@Inject()
httpHelper: HttpHelper;//请求助手
@Get('/')
async listProduct(@Query(ALL) query: ListProductDTO): Promise<void> {
//创建一个查询指令,通过查询总线发送给执行者
const products = await this.queryBus.send(
new ListProductQuery(query.productName, query.page, query.limit)
);
this.httpHelper.success(products, '查询商品成功');
}
}
