ask-sdk v2で複数のスキルから同じDynamoDBを利用する

Alexaスキルを構築・運用する上で永続化された情報は欠かせません。 一般的にはDynamoDBを以下のような形でセットアップして利用することが多いでしょう。 DynamoDBまわりの知識を特に必要とすることがないのはす […]

広告ここから
広告ここまで

目次

    Alexaスキルを構築・運用する上で永続化された情報は欠かせません。

    一般的にはDynamoDBを以下のような形でセットアップして利用することが多いでしょう。

    import * as Ask from 'ask-sdk-core';
    
    const Handler = {
      canHandle(handlerInput) {
        return true
      },
      handle(handlerInput) {
        await handlerInput.attributesManager.setPersistentAttributes({
          hoge: true
        })
        await handlerInput.attributesManager.savePersistentAttributes()
      }
    }
    
    export const alexa = Ask.SkillBuilders.standard()
            .addRequestHandlers(
                Handler,
            )
            .withTableName(config.DB_TABLE_NAME)
            .lambda()

    DynamoDBまわりの知識を特に必要とすることがないのはすごく便利ですね。ですが、スキルの数が増えてくるとちょっと悩ましい点がでてきます。

    そのまま使い続けると起きること

    端的に書くと「スキルの数だけDynamoDB Tableが増える」ということです。リージョンあたりのテーブル数上限は256で、テーブルの数が課金に影響することもないので特に気にすることでもない問題ではあります。

    ただ個人的に「これ、1つのテーブルにまとめれないかな」と思ったので、トライしてみました。

    1つのテーブルを共用するために必要なこと

    ちょっとAWS的な話が入ります。あまり意識せずに使いたいという方は読み飛ばしてください。

    そもそも

    スキルとユーザーIDでユニークな値をとるので、実はそのまま使いまわしても問題ない説があります。ですが万が一かぶったときのことを想定して、以降のステップを紹介します。

    Point1: プライマリキーの扱い

    デフォルトだとプライマリキーはUserIDを利用します。これはスキルとAlexaアカウントの組み合わせでユニークとなりますので、異なるスキルであれば重複することはありません。

    そのため、複数のスキルで共用する場合でもプライマリキーを変更する必要はありません。「複数のスキルでデータを共用したい」という場合は、アカウントリンクなどで別途共用可能なユニーク値を用意する必要があるでしょう。

    Point2: attributeの扱い

    実際のデータはattributesというキーで保存されます。
    参考:https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.ts#L47

    attributesはMap型で保存されます。なので複数のスキルで共用する場合、attributesに保存するオブジェクトのキーを個別に設定してやれば問題ありません。

    const Handler = {
      canHandle(handlerInput) {
        return true
      },
      handle(handlerInput) {
        const attributes = await handlerInput.attributesManager.getPersistentAttributes()
        console.log(attributes.skill1_name)
        await handlerInput.attributesManager.setPersistentAttributes({
          skill2_name: 'John'
        })
        await handlerInput.attributesManager.savePersistentAttributes()
      }
    }

    ただしこの実装では、attributeの名前がスキル別に変わるため、handlerオブジェクトの実装を使いまわしたい場合には向いていません。

    handlerオブジェクトの実装も使いまわしつつ、スキル別に値を取れるようにしたい場合はどうするべきでしょうか?

    DynamoDbPersistenceAdapterでカスタマイズ

    正解は「DynamoDbPersistenceAdapterをカスタマイズする」です。

    import * as Ask from 'ask-sdk-core';
    import { DynamoDbPersistenceAdapter } from 'ask-sdk-dynamodb-persistence-adapter';
    
    export const alexa = Ask.SkillBuilders.custom()
            .addRequestHandlers(
                Handler,
            )
            .withPersistenceAdapter(new DynamoDbPersistenceAdapter({
                tableName: config.DB_TABLE_NAME
            }))
            .withApiClient(new Ask.DefaultApiClient())
            .lambda()

    これでAsk.SkillBuilders.standard()とほぼ等価にすることができます。参考:https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-sdk/lib/skill/factory/StandardSkillFactory.ts#L43-L54

    そしてDynamoDbPersistenceAdapterインスタンスを使うことで、より柔軟にDBを利用することができます。

    import * as Ask from 'ask-sdk-core';
    import { DynamoDbPersistenceAdapter } from 'ask-sdk-dynamodb-persistence-adapter';
    
    // 1つ目のスキル。attributesをデフォルト設定`attributes`で保存する
    export const skill1 = Ask.SkillBuilders.custom()
            .addRequestHandlers(
                Handler,
            )
            .withPersistenceAdapter(new DynamoDbPersistenceAdapter({
                tableName: config.DB_TABLE_NAME
            }))
            .withApiClient(new Ask.DefaultApiClient())
            .lambda()
    
    // 2つ目のスキル。attributesを`sedond-atts`というキーで保存する
    export const skill2 = Ask.SkillBuilders.custom()
            .addRequestHandlers(
                Handler,
            )
            .withPersistenceAdapter(new DynamoDbPersistenceAdapter({
                tableName: config.DB_TABLE_NAME,
                attributesName: 'second-atts'
            }))
            .withApiClient(new Ask.DefaultApiClient())
            .lambda()

    これで「同じテーブルを利用する」と「スキル別に保存するキーを変える」の両方を達成しました。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark