(この記事は DeNAヘルスケア事業本部サイトからの転載です)
2009年にGoogleからリリースされ、身近なアプリにも使われているGo言語。DeNAでも各事業部のエンジニアがGoを用いてさまざまなサービスを形にしています。
そうしたなかで学んだ知識やノウハウを広く共有するために、2019年7月18日(木)、渋谷ヒカリエのオフィスで「DeNA.go #2」というイベントを開催しました。ヘルスケア事業本部からは、DeSCヘルスケアのサーバーサイドエンジニア栗田佳祐(くりた けいすけ)が参加。開発中の新サービスにおけるGoの活用事例について語ってくれました。
※当日のプレゼン資料はこちらからご覧いただけます。
「IT×ヘルスケア」で健康寿命を伸ばす
栗田佳祐(以下、栗田):こんにちは。DeSCヘルスケアの栗田です。よろしくお願いします。まずはDeNAのヘルスケア事業について簡単にお話しします。
DeNAヘルスケア事業では、サービスを通じた健康寿命の延伸を目指しています。
健康寿命とは、介護などを必要とせず、自立した状態で過ごせる期間のこと。日本人の平均寿命は、女性が87歳前後、男性が81歳前後とされていますが、実際にはその数年前、人によっては10年くらい前から寝たきりになってしまうなど、加齢にともなって自分1人で快適に生活するのが難しくなることがあります。
そうした期間をできるだけ短く、人生において健康に過ごせる時間をできるだけ伸ばすというのが僕たちの役割。病気になってから治療するのではなく、多くの人が健康をキープして病気を防げる状態の実現を目指し、DeSCヘルスケアはこれまで3つのサービスをリリースしてきました。
▲登壇資料より抜粋
1つめは、「kencom」。健診結果の閲覧や個々の利用者に合わせた日々の健康情報が閲覧でき、イベントの開催やポイントの付与など、楽しく健康になれる仕組みを備えたヘルスケアエンターテインメントアプリです。
2つめは「さんぽジスタ」というアプリ。DeNAがこれまで培ってきたゲーム開発のノウハウを盛り込んでおり、楽しみながら1日の歩数を増やすことができます。
3つめが「ひさやま天気予報」。疫学的な実証データにもとづいて将来の生活習慣病の発症リスクを提示するとともに、生活習慣の改善効果をシミュレーションすることができます。
ちなみに「ひさやま天気予報」の名前の由来は福岡県の久山町。久山町には九州大学大学院の研究室があり、そこで培った疫学データをお借りしているので、「ひさやま天気予報」と名付けました。
これら3つのサービスに加えて、いま僕たちが新たに開発しているのが体重を管理するダイエットサービス。その新サービスにおけるGoの活用方法や実際に使っているテクニックについてご紹介するのが、今日のメインテーマです。
アーキテクチャとディレクトリ構成
栗田:まず、前提となる技術概要からお話しします。
クラウドについては、DeNAの他の多くのサービスでも使われているGoogle Cloud Platform(GCP)とGoogle App Engine(GAE)の組み合わせです。
GoのバージョンはGo 1.11、データベースはCloud Firestore。
Cloud Firestoreはリアルタイム更新のほか、データベースの作成や更新のイベントをCloud Functionにブックすることができるので、このトリガー機能を使って非同期処理を行なっています。その他、CIツールにはCircle CI、クライアントとの取り決めにはOpen API3を使っています。
▲登壇資料より抜粋
次にGoの具体的な活用方法ですが、僕らが開発しているサービスでは、レイヤードアーキテクチャを採用しています。ディレクトリ構成は、レイヤーの名前通りのディレクトリを切っています。
そのうえで依存性逆転の原則(DIP)を採用しています。「具体は抽象に依存する」という原則から、ドメイン層では抽象的なインターフェイスだけを定義して、具体的な処理はインフラストラクチャ層で行い、依存性を注入しております。
レイヤーとしてはinterface、application、domain、infrastructureの4つ。たとえば、domainであれば、そのなかにドメインモデル(たとえば userなど)ごとにディレクトリをさらに切っています。
また、この4つのレイヤー以外にも設定値を定義したconfigや、どのレイヤーでも利用する共通処理を書いたlibもディレクトリとして切っています。
▲登壇資料より抜粋
レイヤードアーキテクチャとDIの利用方法
栗田:次のスライドに、抽象を具体に依存させる具体的なコードを書いています。
ドメイン層にはインターフェイスしか書いておらず、具体的な処理、どうやってデータを取得するのかについては、インフラストラクチャ層に書いてあります。ドメインに対して具体的な実装をDIしていくというのが、基本的な実装方針になります。
▲登壇資料より抜粋
このDIの構造はライブラリにも用いています。
GoogleAppEngineとGoの組み合わせで使っている方はご存じかと思いますが、GoogleAppEngineはGoのバージョンが、1.9、1.11、1.12とあがるたびに、ドラスティックな変更が入っています。
たとえば最新の1.12のバージョンだと、旧バージョンで使えていたappengineのパッケージが使えません。そういった点も踏まえ、変更が見込まれるライブラリもDIで差し替え可能な状態にしています。
利用できなくなるパッケージを利用することは基本的に避けたいものの、代替のパッケージの利便性がappengineパッケージより著しく劣るものは使えるまで使い続け、いずれ使えなくなったとしても、すぐに対応できるようにしておくという形にしてあります。
▲登壇資料より抜粋(キャプションサイズ)
レイヤードアーキテクチャのメリットとデメリット
栗田:こうしたレイヤードアーキテクチャには大きく2つのメリットがあると思っています。1つは関心事を分離できること。そしてもう1つが、レイヤーごとに変更のライフサイクルが異なる場合に、その変更にうまく適合できることだと思っています。
▲登壇資料より抜粋
サービスを運用していると、機能要件によってドメイン層の仕様がころころ変わるというのはよくある話なんですが、レイヤードアーキテクチャの場合、ドメイン層のロジックが変わったとしても、インフラストラクチャ層には伝播しません。
逆もまた然りで、クラウド側の都合でインフラストラクチャ層が変わっても、ドメイン層は影響を受けません。1つのレイヤーの変更がそこだけで完結し、他のレイヤーを変更しなくても済むのは、開発・運用していくうえで非常に大きなメリットです。
その一方、レイヤードアーキテクチャを採用すると、どうしてもコードは増えます。シンプルなAPIの実装でも、各レイヤーで関数を定義する必要があるため、コード差分が大きくなってしまい、その結果として書くテストも増える。そういう傾向は確かにあると思います。
その対策としてDeSCヘルスケアでは、gomockを使ったり、テストで自動生成したりすることで、工数を軽減しています。
たとえば、インターフェイス層はレスポンスのステータスコードやレスポンス形式のテスト、アプリケーション層はドメインからのインターフェイスへのエラー伝播のテスト、ドメイン層はドメインロジックそのもののテスト、といったように、レイヤーごとの責務に対して最小限のテストだけで済むような形で実装しています。
エンベロープ暗号によるカラムの暗号化
最後は暗号化について。冒頭でもお話したとおり、ヘルスケアサービスでは健診情報などデリケートな情報を取り扱うため、セキュアな仕組みづくりがかかせません。
僕たちのプロジェクトでは、データベースの取得時・保存時に透過的に処理を行なう関数を挟むようにしたうえで、エンベロープ暗号も採用し、弊社のセキュリティポリシーに合わせてカラムまで暗号化しています。
▲登壇資料より抜粋
その際、非常にシビアになってくるのが、鍵の管理です。これについてはKMS(Key Management System)を利用し、鍵の管理を行っています。
具体的には、鍵を暗号化する鍵(KEK=Key Encryption Key)と、データを暗号化する鍵(DEK=Data Encryption Key)の2種類を使っています。このうちKEKをKMSを利用して管理しており、アプリの初期化時に復号したKEKをメモリ上にのみロードするようにしています。
そのうえでデータ保存時には、カラムごとにデータを暗号化するDEK(Data Encryption Key)を作成し、メモリ上にロードしたKEKによってDEK自体も暗号化して保存するという手法です。
全ての値をKMSで暗号化/復号すれば鍵の管理をKMSに任せられ、よりセキュアになるのですが、データの件数が増えるとその分KMSへのアクセスが増え、パフォーマンスとコストが問題になってきます。
エンベロープ暗号によってKMSへのアクセス回数が抑えられるようになり、開発のパフォーマンスとセキュリティを両立することができました。
まとめ
栗田が担当している新サービスの開発では、レイヤードアーキテクチャとDIを利用することで変更に強い構成にしたうえで、コードが増えるという課題に対してはmockなどを活用して対応。さらにエンベロープ暗号によって、セキュリティとパフォーマンスを両立しています。
サービスの要件や開発環境はさまざまですが、Goを使っているエンジニア、レイヤードアーキテクチャの採用を検討しているプロジェクトの担当者の方には、1つのヒントになったのではないでしょうか。
DeNAヘルスケアでは、今後も今回のようなイベント参加や記事の配信を通じて、サービス開発のノウハウや成功事例を共有していきます。
※オリジナルのGopherはRenée Frenchによってデザインされ、CC BY 3.0ライセンスが適用されています。
<登壇者プロフィール>
栗田 佳祐(くりた けいすけ)|DeSCヘルスケア株式会社 サーバーサイドエンジニア
2012年、DeNAに新卒入社。Perlをメインとしたブラウザゲームの開発を担当する。その後2015年に現職のDeSCヘルスケアへ異動。健保組合向けサービス「KenCoM」を経て、現在は体重管理系の新サービスを開発中。SNSのハンドルネームはkurikei。
執筆:斉藤良 編集/撮影:八島 朱里
※本記事掲載の情報は、2019年8月20日時点のものです。