Kipp Engineering Blogを始めます

xyz

Kipp Engineering Blogを始めますの目次

  • Kippという会社について
  • BaaSとは
  • なぜBaaSとして提供するのか
  • どのようにBaaSを提供しているのか
  • 管理画面について
  • gRPCメソッドのリクエストにGraphQLクエリをのせる
  • 終わりに

はじめまして。xyzです。

この度プロダクトのローンチを受け、またKipp社内で取り扱っている技術の知見やノウハウの発信を目的として、エンジニアブログを始めるに至りました。 ブログを通して組織の雰囲気やKippという会社に興味を持っていただければ幸いです。

私自身の最初の投稿でもあるので簡単に自己紹介をします。 中高時代からHTMLやCSS・JS・Androidアプリ開発を独学し、大学では情報系の学部を卒業しました。 卒業後はアルバイトとして働いていた会社にそのまま就職後、転職を二度経験し現在に至ります。 前職、前々職ともフロントエンドエンジニアとして業務に携わっていました。 私個人としての主な興味はフロントエンドですが、Kippではフロントエンド・バックエンドと言った区分はなく、フルスタックエンジニアという肩書でバックエンドやインフラストラクチャにも携わっています。 少し前までは実質バックエンドエンジニアと言っていいほどもっぱらバックエンドの実装をしていました。 最近ではこのブログの構築等、フロントエンドが多くなっています。

さて、記念すべき1エントリ目ではKippが何をやっている会社なのかと、ある少し変わった実装について紹介します。

Kippという会社について

Kippでは現在、主にBanking as a Serivce(以下BaaS)と呼ばれる金融サービスを開発・運用をしています。

弊社オフィスは日本の金融の中心街である東京都大手町の、数多くのFinTechベンチャー企業が集うFINOLAB内にあります。 オフィスは東京ですが、CEOは福岡県、CTOは北海道在住と柔軟な働き方ができる会社です。 エンジニアはコアタイムなしの完全フレックス制かつ完全リモートで、勤務日も柔軟に調整が可能です(稀に出社が必要になることがあります)。 業務において、普段のコミュニケーションはSlackを使ったテキスト中心の非同期なやりとりがほとんどです。 時間が取られがちである会議など同期的なものは極力避けて業務を遂行できるような環境・仕組み作りを行っています。

私自身、このような環境で働き始めてから半年以上経ちますが、プライベートとの両立がしやすくなり助かっています。 普段の生活に必要な買い物などでも気兼ねなく外出できますし、病院などが予約が必要な予定も入れやすいです。 普段の業務であれば離席時に逐一報告等も必要なく、仕事中の外出の心理的障壁が低いのも利点です。 仕事の合間に散歩してリフレッシュしつつ、何食わぬ顔でまた業務に戻る…という雰囲気で自由にやらせてもらっています。

BaaSとは

BaaSとは多岐にわたる金融サービスをワンストップで提供する中央集権的なSaaS (Software as a Service) です。 利用企業やエンドユーザーはそれぞれのAPIを通じてBaaSを利用します。 すでにリリースされているサービスではプリペイドカードの発行と残高管理・決済・送金機能・債権管理など多数の機能を提供しています。

なぜBaaSとして提供するのか

これまで金融サービスの開発と運用は利用企業ごとにデザインやワークフローをカスタマイズしたい需要が強いもので、一社一社に合わせて製品を提供していました。 しかし、金融サービスの提供事業者にとっては開発に時間とコストがかかるだけではなく、その基盤の上で新機能の追加や外部連携といった機能を追加するのは更にコストがかかってしまいます。 Kippはカスタマイズをした製品を売るのではなく、どんな企業にも受け入れられる機能セットを備えた1つのサービスを提供します。 利用者はAPIを通して提供したい機能を集中して開発し、エンドユーザーにサービスを届けることができます。

どのようにBaaSを提供しているのか

Kippのコア部分は主に定期的なジョブを処理するプログラムとAPIサーバとして処理を待ち受けるプログラムで成り立っています。 定期的なジョブはプリペイドカードの発行処理・財務局への提出が必要な日次・月次レポートの作成など多岐にわたります。 また、よく使われる管理機能を提供するウェブアプリケーション(管理画面)があります。 管理画面はカスタマイズのニーズが少なく、低いイニシャルコストで効率の良いものを使えることが望まれるので標準提供しています。

以上を踏まえて、今回は一例としてKippの管理画面を交えながら、技術スタックと少し変わった実装について軽く触れて終わりにします。

管理画面について

変わった実装の前に軽く技術スタックを紹介します。

管理画面のバックエンドとなるAPIサーバーはScalaで実装され、データベースにはAurora MySQLを利用しています。 バックエンドではPlay Frameworkのようなフルスタックフレームワークを用いず、データベースとの接続にSlickを、外部との通信は主にgRPCを利用するシンプルな構成になっています。 また、REST APIにおける純粋なGETのようなデータの取得がメインになる操作についてはSangriaと呼ばれるGraphQLライブラリを介してデータの取得、レスポンスをしています。

次に実際にユーザーが操作することになるフロントエンド部分についてです。 言語はTypeScriptを採用しており、フレームワークは古いプロジェクトではNuxt.js、最近のプロジェクトではNext.jsを採用しています。 管理画面のUIにはVuetifyMaterial-UIと言ったマテリアルデザインベースのUIライブラリを採用しています。 UIライブラリは無数にありますが、マテリアルデザインベースのものを選択したのには3つ理由があります。

  1. 既存のコンポーネントが充実しています。 今日UIとしてありがちなモーダル・テーブル・カードなど必要不可欠なものは一通り揃っています。 これらを組み合わせることで開発速度を上げることができます。
  2. 実装方針が示されています。 既存コンポーネントが充実しているだけではどのように使うことがユーザーにとって最適なのかが判断しづらく、ときにちぐはぐなUIを構築してしまうことがあります。
  3. マテリアルデザインというそこそこメジャーなUIを提供することで、使用者が既に同じようなUIを操作している可能性が高くなり、どんな操作をすれば良いかを自然と判断できることが期待できます。

3つ目に関しては、マテリアルデザインを使うとどうしても同じように見えて退屈に感じてしまうデメリットとも読み取れるので、コーポレートサイトなどブランドを前面に出したい場合は少し難しい場合もあります。 しかし、カスタマーに直接見えない管理画面の類ではあまり気にする必要がないですし、綺麗さ・使いやすさ・開発スピードのバランスが取れた優秀なUIと言えるでしょう。

gRPCメソッドのリクエストにGraphQLクエリをのせる

フロントエンドアプリケーションである管理画面はバックエンドとの通信の際、取得にはGraphQL、作成・更新にはgRPCという少し変わった戦略を取っています。 正確に言えば、データを取得する際のGraphQLのクエリもgRPCのリクエストの文字列として送信・受信をしているので、基本的には全てgRPCの上で通信していることになります。

なぜこのような実装になっているかと言うと、フロントエンドがデータの取得に関しては画面の構成に応じて柔軟に関連するデータを組み合わせることを要求することが理由です。 逐一クライアント側に合わせてgRPCのメッセージを作っていてはキリがありませんし、protoを変更すればサーバーとクライアントの両方を変更する必要があり、開発者の手間が増えます。

例としてユーザー情報を取得するアプリを考えてみます。

service UserAPI {
  rpc fetch(Request) returns (Response) {}
}

message Request {
  string id = 1;
}

message Response {
  string id = 1;
  string name = 2;
  string email = 3;
  // DBのフィールドが増え、クライアントにも必要になったとき
  // 逐一サーバーとクライアントで変更をする必要がある
}

コメントにあるように、フィールドが増えたときに変更を要します。

更にここからユーザーが記事を投稿できるような機能が追加され、ユーザー情報取得時に記事も取得したくなったとき、このような変更を加えることになるでしょう。

service UserAPI {
  rpc fetch(Request) returns (Response) {}
}

message Request {
  string id = 1;
}

message Response {
  string id = 1;
  string name = 2;
  string email = 3;
  repeated Article articles = 4;
}

message Article {
  string title = 1;
  string body = 2;
}

gRPCメッセージ自体に関連を定義してしまうと、今後クライアントの画面構成次第でprotoファイルに変更をする可能性があります。 その結果、サーバーでも実装の変更が発生するのは手間がかかってしまいますし、ナンセンスと言えるでしょう。 また、Webアプリでは記事情報が必要だけどモバイルアプリでは必要ない、と言った状況ではフィールドをoptionalにするのも、似たようなAPIをもう1つ生やすのも良い実装に感じられません。

そこで、データの取得に関してはGraphQLのデータをやり取りをするgRPCメソッドを用意することで、クライアントは自由にデータの型を定義できるようになります。 gRPCメソッドのリクエストとレスポンスはstring型のフィールドを持つシンプルなメッセージです。 サーバーは、リクエストに乗っているGraphQLのクエリ文字列(ミューテーションは含みません)の処理をSangriaで行い、結果をJSON文字列として返します。

service GraphQLAPI {
  rpc query(Request) returns (Response) {}
}

message Request {
  // 欲しいフィールドのGraphQLクエリをqueryの中に文字列として格納する
  string query = 1;
}

message Response {
  // queryに基づいたJSON文字列が返ってくる
  string result = 1;
}

こうすることでサーバーとクライアントの間でgRPCとしての型を気にする必要はなくなり、分担して開発を進めることが可能になります。 勿論、サーバーの実装としてDBのカラム情報をgRPCのフィールドへのデータの詰替え作業がなくなった分、GraphQLとしてのリレーションを定義する必要性があります。 ですが、interfaceの実装が必要なgRPCメソッドを直接いじらずにサーバーとクライアントが各々に作業できるのは利点でしょう。 これはある種のCQRS (Command Query Responsibility Segregation) 的発想と言えます。

さらにリレーションの定義はScalaの機能を生かした内製ライブラリで大幅に簡略化できています。 このライブラリについては次回ご紹介します。

終わりに

今回は少ない作業量で高品質な管理画面を提供するコツを紹介しました。 次回はGraphQL APIを提供するサーバ側の仕組みをより詳細に紹介する予定です。 今後もKippのテクノロジーや開発チームについて続々と情報発信していきますので、ぜひお読みください。 弊社に興味をもたれた方はお気軽にpeople@kipp-corp.comにコンタクトください。

今後ともよろしくお願いします。