MBC CQRS サーバーレス フレームワーク TO-DO システム作成 4 – 特定のデータの読込
このサンプルでは、DynamoDBとRDSへデータをそれぞれ書き込みを行っております。
特定のデータを読み込むにはどちらのデータを読み込むべきか?
特定のデータを読み込む際にはDynamoDBを使用することにします。
理由はRDSのスケーラビリティは限界がありのに対して、DynamoDBで特定のデータを読み込むのは得意であると言う特徴が有るためです。
では、早速特定のデータの読込の実装を行ってみましょう。
サービスの実装
src/todo/todo.service.ts
に DataService
を注入します。
constructor(
private readonly commandService: CommandService,
private readonly dataService: DataService,
) {}
以下のように入力しデータ読み込み処理を追加します。
async findOne(detailDto: DetailDto): Promise<TodoDataEntity> {
const item = await this.dataService.getItem(detailDto)
if (!item) {
throw new NotFoundException('Task not found!')
}
this.logger.debug('item:', item)
return new TodoDataEntity(item as TodoDataEntity)
}
最終的に src/todo/todo.service.ts
は以下のようになります。
import {
CommandService,
DataService,
DetailDto,
generateId,
getUserContext,
IInvoke,
VERSION_FIRST,
} from '@mbc-cqrs-serverless/core'
import { Injectable, Logger, NotFoundException } from '@nestjs/common'
import { generateTodoPk, generateTodoSk, TODO_PK_PREFIX } from '../helpers'
import { CreateTodoDto } from './dto/create-todo.dto'
import { TodoCommandEntity } from './entity/todo-command.entity'
import { TodoDataEntity } from './entity/todo-data.entity'
@Injectable()
export class TodoService {
private readonly logger = new Logger(TodoService.name)
constructor(
private readonly commandService: CommandService,
private readonly dataService: DataService,
) {}
async create(
createDto: CreateTodoDto,
opts: { invokeContext: IInvoke },
): Promise<TodoDataEntity> {
const { tenantCode } = getUserContext(opts.invokeContext)
const pk = generateTodoPk(tenantCode)
const sk = generateTodoSk()
const task = new TodoCommandEntity({
pk,
sk,
id: generateId(pk, sk),
tenantCode,
code: sk,
type: TODO_PK_PREFIX,
version: VERSION_FIRST,
name: createDto.name,
attributes: createDto.attributes,
})
const item = await this.commandService.publish(task, opts)
return new TodoDataEntity(item as TodoDataEntity)
}
async findOne(detailDto: DetailDto): Promise<TodoDataEntity> {
const item = await this.dataService.getItem(detailDto)
if (!item) {
throw new NotFoundException('Todo not found!')
}
this.logger.debug('item:', item)
return new TodoDataEntity(item as TodoDataEntity)
}
}
コントローラの実装
src/todo/todo.controller.ts
に以下のように入力しAPIを実装します。
@Get('/:pk/:sk')
async findOne(@Param() detailDto: DetailDto): Promise<TodoDataEntity> {
return this.todoService.findOne(detailDto)
}
最終的に src/todo/todo.controller.ts
は以下のようになります。
import { DetailDto, IInvoke, INVOKE_CONTEXT } from '@mbc-cqrs-serverless/core'
import { Body, Controller, Get, Logger, Param, Post } from '@nestjs/common'
import { CreateTodoDto } from './dto/create-todo.dto'
import { TodoDataEntity } from './entity/todo-data.entity'
import { TodoService } from './todo.service'
@Controller('api/todo')
export class TodoController {
private readonly logger = new Logger(TodoController.name)
constructor(private readonly todoService: TodoService) {}
@Post('/')
async create(
@INVOKE_CONTEXT() invokeContext: IInvoke,
@Body() createDto: CreateTodoDto,
): Promise<TodoDataEntity> {
this.logger.debug('createDto:', createDto)
return this.todoService.create(createDto, { invokeContext })
}
@Get('/:pk/:sk')
async findOne(@Param() detailDto: DetailDto): Promise<TodoDataEntity> {
return this.todoService.findOne(detailDto)
}
}
データ読込のテスト
データの読込を行う際、pk/sk が必要となります。sk がランダムになっているためメモが取っていない限りskが何であるか分かりません。
そのため、ローカルのDynamoDB Adminにアクセスをして pk/sk を取得します。
http://localhost:8001 にアクセスをして local-demo-todo-data をクリックします。
TO-DOのデータの pk
と sk
を取得します。
次に以前作成したテストに以下を追加します。
test/api.http
を開き以下を追加します。
### Get Specific To-do item
GET {{apiBaseUrl}}/api/todo/pk/sk
Accept: application/json
Content-Type: application/json
Authorization: {{ADMIN_TOKEN}}
X-Tenant-Code: MBC
上記ソースの pk
と sk
の部分に ローカルのDynamoDB Adminで取得した pk
と sk
を URLエンコードをしてセットします。
URLエンコードして pk
と sk
をセットした例は以下の通りです。
### Get Specific To-do item
GET {{apiBaseUrl}}/api/todo/TODO%23MBC/01JAFJHS7KM8ZM50X5CX4T6517
Accept: application/json
Content-Type: application/json
Authorization: {{ADMIN_TOKEN}}
X-Tenant-Code: MBC
トークン取得してから上記を実行すると下記の様な結果を得ることが出来ます。
HTTP/1.1 200 OK
x-powered-by: Express
access-control-allow-origin: *
content-type: application/json; charset=utf-8
content-length: 584
etag: W/"248-lGnS/uXnNO2cjABH1/5N+Y364VY"
cache-control: no-cache
accept-ranges: bytes
Date: Sun, 20 Oct 2024 01:35:44 GMT
Connection: close
{
"code": "01JAFJHS7KM8ZM50X5CX4T6517",
"updatedBy": "92ca4f68-9ac6-4080-9ae2-2f02a86206a4",
"createdIp": "127.0.0.1",
"tenantCode": "MBC",
"type": "TODO",
"version": 1,
"createdAt": "2024-10-18T10:25:10.000Z",
"updatedIp": "127.0.0.1",
"createdBy": "92ca4f68-9ac6-4080-9ae2-2f02a86206a4",
"requestId": "e2e26c07-0b12-496d-b3e4-92247cdc55d7",
"sk": "01JAFJHS7KM8ZM50X5CX4T6517",
"name": "Test Task 1",
"attributes": {
"description": "desc",
"status": "PENDING"
},
"csk": "01JAFJHS7KM8ZM50X5CX4T6517@1",
"pk": "TODO#MBC",
"id": "TODO#MBC#01JAFJHS7KM8ZM50X5CX4T6517",
"cpk": "TODO#MBC",
"updatedAt": "2024-10-18T10:25:10.000Z"
}