Wave / アプリチームの猪飼 (いかい) です。 Nikkei Advent Calendar 2020 の 18 日目の記事として、今年 7 月にリリースした Nikkei Wave アプリについて簡単にご紹介します。
Nikkei Wave とは
ニュースの新しい読み方を体験できるアプリです。Wave チームでは、日経電子版の非連続な進化を目指して、新しい機能やアイデアの実験・開発を行っており、この成果をユーザーの皆さんに一足先に体験してもらうために Nikkei Wave アプリ (以下 Wave) を公開しています。良い反響があった機能については、日経電子版アプリに追加する方針です。
現時点では、日経ID登録済みの日経電子版有料会員が利用でき、後述する機能を提供しています。iOS (iPhone / iPad) 版を先行提供中です。Android 版は目下開発中です。

主な機能
レコメンド
レコメンドされた記事が Wave のメインコンテンツであるタイムライン上に表示されます。タイムラインをスクロールしていくだけで、興味のあるニュースを読めるシンプルな体験を目指しています。
ユーザーの過去の閲覧傾向や、同様の記事をもっと読みたい / 読みたくないを示すインタラクションによって、新しく開発されたレコメンド基盤がユーザーごとに嗜好を学習し、より的確に記事を推薦できるようにもなります。

Wave におけるレコメンドについては AWS Dev Day のセッション を参照してください。
自動生成された動画の再生
Advent Calendar 7日目の記事 でも言及されていますが、記事から自動で動画を生成し、タイムラインの中で再生できる機能があります。

動画だけを見ていくモードもあり、Instagram のリールや TikTok のような要領でニュースを見られます。

その他の機能
- RSS フィードのフォロー
- 購読した RSS フィードの記事はタイムラインで表示されるほか、フィード毎にまとめて読むこともできます。
- ブックマーク
- 後で読みたい記事や保存しておきたい記事などをブックマークして後から読み返せます。
- 閲覧履歴
- 閲覧した日経電子版の記事やRSS フィードの記事をまとめて表示します。
設計
日経電子版の各サービス (スマホアプリ、Web サイトなど) は、おおよそ Microservices + APIGW (API Gateway) + BFF (Backend For Frontend) という構成になっています。機能単位の Microservice が APIGW の後ろにおり、APIGW がそれらを束ね、機能ごとの認可を行います。BFF は APIGW へのプロキシの役割に加えてサービス固有の機能の提供を担います。

Wave においても同じ構成をとりました。上の画像で背景色がある部分が Wave のために用意されたものです。日経電子版アプリでは BFF ↔ フロントエンド間は REST API を利用していますが、Wave では gRPC を選択しました。
BFF は Go で書かれており、iOS アプリは Swift、Android アプリは Flutter で開発しています。
Wave 用に新しく作ったレコメンド基盤は BFF から APIGW を介さずに直接利用しています。これを他のサービス (日経電子版アプリ、Web など) から使うようになったときには APIGW から利用できるようにします。
gRPC
日経電子版の記事データには多くのフィールドがあります。gRPC (Protobuf) を使うことで、BFF からデータを受け取り、多くのフィールドをクライアントの実装でマッピングする手間を省くことができます。また、必要なパラメーターをセットして、BFF にリクエストして、レスポンスを受け取って処理を行う、一連のフローの実装の雛形を生成してくれます。この点で、BFF とクライアント間の通信において、実装の手間を減らせたうえ、データ型の不整合を理由としてデバッグにとられる時間も減らせました。Go, Swift, Flutter (Dart) ともにクライアントライブラリ (grpc-go, grpc-swift, grpc-dart) を利用しています。
GKE
今回新しく用意した Wave の BFF では、初期段階では CircleCI 上でビルドした go バイナリを AWS SSM 経由の rsync で EC2 に流して実行していました。このままでは各種のログが見にくい、デプロイで失敗すると復旧が面倒、スケールしないなどの理由で、これらを平易に解決できる乗り換え先を探しました。
そこで、既に日経電子版の Web サイトで使われており、SRE チームが開発している GKE 基盤 に移行することにしました。gRPC / TLS 対応、オートスケール、通信ログ・アプリケーションログも備えており、Go でのアプリケーション実装に集中できる環境になりました。
Firebase
また Wave 固有のデータの保存やホスティングについては iOS / Android アプリと一緒に使いやすい Firebase を選びました。Cloud Functions や Firestore などは従量課金なので、不必要にコールすることが無いように意識しておきたいところです。Firestore は最近 !=
や not-in
のようなクエリも追加され、より柔軟性が上がり使いやすいものになってきています。MAU は数千人ほどの規模であって、定期的な Cloud Functions の実行もスケジュールされていますが、Firestore にかかるコストも含め、かなりの低コストで運用できています。
Wave では、認証と認可を APIGW に任せるシンプルで楽な形にしたかったことと、リアルタイムアップデートを必須とする要件が多くなかったため、全て BFF を介して Admin SDK 越しに利用しています。
クライアント
Wave は日経電子版アプリに先行して比較的新しい技術を試用する目的もあり、iOS, Android ともに比較的新しい OS を対象とし、新しい OS のみで使える API を積極的に利用しています。iOS アプリでは、 SwiftUI や、 UICollectionViewCompositionalLayout などの新しい API をアプリ内の各所で利用し実装しています。
自動動画生成と WebView
Wave には、記事から文章や数値を抽出し適切な画像と組み合わせて仕立てられた動画を表示する機能があります。
記事から動画データへの変換はある程度の処理コストがかかります。変換が一定のルールに基づいたものであれば、各々の端末上でそれを行うのは非効率的ですし、iOS / Android 向けに同じ実装を行うのも面倒です。このため、記事情報から動画用のデータを生成する API (Auto Video API) を開発しています。
記事の ID を指定して Auto Video API にリクエストすると、API は動画の素ファイルとなるデータをレスポンスします。

日経電子版アプリでは記事本文の表示に WebView を利用しています。Wave においても記事本文の表示に WebView を利用しているほか、この動画の再生にも WebView を利用しています。
アプリは一定の間隔で Firebase Hosting に置かれた、動画再生用のテンプレートファイルを pull します。また、適切なタイミングで API から動画のデータを受け取り、それをテンプレートファイルに埋め込み、WebView に渡して再生します。動画のテンプレートファイルは React で書かれており、JS, CSS を組み合わせてアニメーションを実現します。

iOS, Android など複数のプラットフォームにおいて、複雑な表現を行いたい場合、ネイティブコード (iOS なら Swift / Objective-C, Android なら Kotlin / Java) で各々実装していく選択もありますが、かなりの工数がかかってしまいます。HTML / CSS / JS であれば実装を共通化できます。部分的な WebView の導入により、実装の手間を減らしながらも、ネイティブアプリの滑らかな操作感を維持し、ジェスチャーやアプリ固有の API への平易なアクセスも確保できます。
一方で、今回紹介したユースケースにおいては、JS や CSS によるアニメーションが多くあり、一画面で複数の動画が再生される状況もあるため、端末への負荷が高まりやすいです。端末のパフォーマンスを気にかけて実装する必要があります。
おわりに
この記事では Nikkei Wave について簡単にご紹介しました。現在もいくつかの新機能を実装中で、近日中のアップデートでリリースされる予定です。ご期待ください。また、新しいアイデアや、API やツールなどを組み合わせて、新しいニュースサービス体験を作りたい、日経電子版にこんな機能がほしい、という方、ぜひ一緒に作っていきましょう。