ServerlessDays福岡でDevelopers.IO CAFEのバックエンドについて話しました #serverlessdays #serverlessfukuoka
2019/12/14(土)に開催されたServerlessDays 福岡で「キャッシュレスカフェのバックエンドを支えるサーバーレスアーキテクチャ」というタイトルで発表をしてきました!
スライド
発表内容
Developers.IO CAFEとは?
秋葉原にあるキャッシュレスカフェでモバイルオーダーとウォークスルーといったサービスを提供しています。
モバイルオーダー
スマホから注文と決済までが完結して、店舗ですぐに商品が受け取れるサービスです。
ウォークスルー
スマホのアプリ上のQRコードをかざして入店、商品をとって退店をすると自動で商品を判別して決済できるサービスです。
カフェプロジェクトの歴史
シーズン1シーズン2は例のAmazonGOを作ってみたという形で、ウォークスルーがプロトタイプとして完成しました。
そのあとのシーズン3では実際にカフェがオープンし、モバイルオーダーを導入しました。
さらにシーズン4ではシステムをプラットフォーム化して多店舗展開を実現しました。
実際に展開しているところでは、Developers.IO CAFE 上越店 や、昭和女子大で運営しているダイエースマートストアなどに導入されています。
全体構成図
- 認証にはAuth0
- 決済サービスはstripeとLINEPay
- ウォークスルーはStep Functionsを使ってステート管理
- ダッシュボードはAngularで実装していて、CI/CDツールとしてCircleCIを採用
- 対してAPI側は権限が増えるのでIAMロールで操作ができるCodePipelineを採用
カフェシステムにおけるマルチテナントの定義
まずサービスの中に複数の認証基盤が存在します。
Auth0だと1つのユーザー基盤が1テナントと定義されるのに対して、カフェでは認証基盤の単位をドメインと呼んでいます。
さらにドメインの中に企業毎に登録するテナントという概念が存在します。
なのでサービスの中に複数のドメインが存在して、さらに1つのドメインの中に複数のテナントが存在、テナントの中には複数の店舗が存在することになります。
例
- ドメイン: Auth0テナント
- テナント: Developers.IO CAFE
- 店舗: Developers.IO CAFE 秋葉原店, Developers.IO CAFE 上越店
権限管理
権限は大きく3つに分類されています。
強い順にサービス全体の権限を持つAdminとテナントの配下に対して操作ができるテナント管理者と、店舗毎のオペレーションが実行できる店舗管理者という3段階となっています。
認証
シングルテナント版の時はCognitoを利用していましたが、機能の豊富さなどもありAuth0に移行しました。 実際にカフェシステムで使っている機能をいくつか紹介します。
- API GatewayのLambdaオーソライザー
- Rulesでサインアップ時の処理をカスタマイズ
- ウォークスルーのハードとのM2M通信
- Rolesを使った権限管理
Auth0を使った権限管理
先程説明した権限管理をAuth0のRolesとMetadataを使って実現しています。
- ADMIN
- ADMIN用のRolesを適用
- テナント管理者
- テナントIDのRolesを適用
- 店舗管理者
- テナントIDのRolesを適用、さらにMetadataに店舗IDを適用
APIのステージング
カフェのシステムではリリースサイクルが早いので、随時アップデートしている current
とは別に、 stable
という安定版の実行環境を用意しています。
それぞれのサイクルはcurrent最新版であれば週一程度でリリースして実運用を経てブラッシュアップしたものを、安定版に月一程度でリリースしています。
実際の分岐の方法としては、APIGatewayのステージとLambdaのエイリアスを使って実行環境を切り替えています。
ウォークスルーの仕組み
ウォークスルーの仕組みをざっくりと説明します。 まず、センサーが2つあります。1つ目はToFセンサーを使った人物検知で、2つ目が自社開発の重量センサーを使った商品の増減判定です。
流れとしては、 - QRコードをかざして入店 - センサーからのデータをAWS IoTにMQTTで送信 - 退店 - Step Functionsで最終的な商品判定と決済
となります。
DynamoDB
DynamoDBの機能を活用して効率的かつ安全に処理をしています。
- シーケンス(アトミックカウンター)
- 注文番号やユーザーの来店数をカウント
- TTL
- クーポンやトークンの自動削除
リソースの分け方
まず、テーブルが大きくサービス全体で共通のリソース、ドメイン毎に共通、テナント毎に共通の3つに分割されます。
例えば共通リソースであればドメインやテナント、さらにシーケンステーブルも共通リソースにしてインクリメントしたい項目は全てシーケンステーブルにも切り出すという処理をしています。
シーケンスの活用例
これはスタッフ向けのダッシュボードの注文一覧画面なんですが、実際にシーケンスを活用しています。
ユーザーの来店回数をカウントしているので、初回来店が目視できて(四つ葉のマーク)店舗スタッフの方が案内しやすくなるという利点があります。
こちらの実際のDynamoDBの設計は以下のようになっています。
パーティションキーに店舗ID、ソートキーにユーザーIDを指定します。
インデックスには、GSIのパーティションキーにユーザーIDを指定することでユーザーがトータルでMOとWTをそれぞれ何回使っているのか、が分かるようになります。 LSIにはソートキーにTimestampを指定することで特定の日時や期間で店舗毎の来店数がどのくらいだったのか、という風にデータが取得できるようになります。
このように、どのような検索をかけたいのかというのを洗い出して設計に当てはめていきます。
実装例
領収書
要件は主にこの3つです。
- エンドユーザーがWebで確認できること
- メールで領収書送信
- 店舗毎にフォーマットを変えられるようにする
バックエンドは Node.js なので、テンプレートエンジンにEJSを使いました。
テンプレートは店舗IDでパスを切ってS3に保存します。
さらに生成したHTMLをS3に保存、SESの sendEmail
でメール送信を実装しました。
軽減税率
カフェシステムでは10月の税率変更に伴い、軽減税率を導入しました。
その際に、制度自体が複雑な上実例がないのでユースケースがわからないという課題に当たりました。
これをTDDで実装することで、テストケースをそのままユースケースに当てはめでことが実現できて効率的かつシンプルにすることができました。
まとめ
カフェプロジェクトはリリースサイクルを高速で回すことで、顧客のフィードバックを高速に反映しています。
さらに CircleCI・Stripe・Auth0などのSaaSサービスを用いてSaaSサービスの開発を加速化しています。
三種の神器、どんどん使っていきましょう!
また、AWSや他のサービスもアップデートにより最適な構成は日々変化します。
カフェで使っているStep Functionsも、Expressタイプが新発表されたので試してみたいと思います!
さいごに
サーバーレスの精鋭達に囲まれての発表だったのでど緊張でしたが、とてもいい機会を頂けました。
ご来場くださった方、スポンサー・運営に関わって下さった方本当にありがとうございました!!