Assuredの技術スタック紹介(アプリ編)

こんにちは!エンジニアの内山(@uchidas)です。
今回は「Assured」のアプリ側(フロント/バックエンド)の技術スタックについてご紹介していきたいと思います。
なお、今回は技術についての説明(ex. Scalaとは?)や、選定理由については触れません。選定理由については以下の記事で触れていたり、今後書いていくものもありますので合わせて見ていただけるとうれしいです。

「Assured」の技術スタックの全体像

「Assured」では以下のような技術スタックになっています。そして、今回の記事はアプリ編ということで赤枠部分について書いていきたいと思います。

Assuredの技術スタック

「Assured」は大きく分けてクラウドサービス利用企業様(Client)、クラウドサービス事業者様(Provider)、管理者(Admin)の3つのユーザーがいて、それぞれのアプリケーションがAPIとSPA(Single Page Application)を返すWebで構成されています。これらの他にバッチ処理を扱うアプリケーションなどもあります。

アプリケーションの概要図

これら複数のアプリケーションはMonorepoで運用しています。現時点ではエンジニアチームの人数も少なく、全員がフロントからインフラまで横断的に実装しているので分割する理由がないのですが、今後分割を検討するにしても依存関係のある各アプリケーションの変更を一括で扱えるというメリットがあります。

バックエンドについて

Scala + Play Framework

APIの開発言語はScalaを採用していて、WebフレームワークはPlay Frameworkを利用しています。そして、先ほど紹介した各アプリケーションに加えて共通のロジックを切り出すlib-apiというプロジェクトを各アプリケーションが参照する構成になっています。

プロジェクトの参照関係(API)

実装レベルではPlay FrameworkのDI(Dependency Injection)の仕組みを利用して、以下の様なシンプルなレイヤードアーキテクチャを採用しています。こちらは、Ruby on RailsJavaのSpring Bootなどをやっていた方はよく目にしたことがある構成かと思います。

レイヤードアーキテクチャ

なお、「Assured」のエンジニアチームは各自得意な開発言語が異なり、私の様にScala初学者もいます。そのため、Scala初学者でもプロジェクトにジョインしやすいようにオンボーディングやコードなどを工夫しています。これらの取り組みについては以下の記事で書いていますので、ぜひこちらも合わせて見ていただけるとうれしいです。
tech.assured.jp

Slick + PostgreSQL

データベースはPostgreSQLを利用していて、現時点ではマイクロサービス化はしておらず各アプリケーションは同一のデータベースを見ています。
データベースのスキーマ定義はマイグレーションツールを使っていて、データベースへの変更は差分をレビューしやすいようにしています。
ERD(Entity Relationship Diagram)もSchemaSpyを用いて現在のスキーマから生成するようにしています。
また、アプリケーションからのデータベースへのアクセスはO/RマッパーのSlickを利用しています。

テストについて

新規事業では事業がピボットする可能性があったり、組織が大きくなるときにはチームメンバーのスキルセットの変化などもあるかと思います。その時にリライト・リアーキテクチャの選択肢を残して柔軟に対応できるように、捨てやすいコードというものを意識しています。

捨てやすいコードの取り組みの1つとして、仮にリライト・リアーキテクチャとなった時にテストが仕様書となるため、少なくともビジネスロジックを扱うServiceレイヤーは正常系、異常系のテスト、Controllerレイヤーは正常系のテストを必須としています。

フロントエンドについて

React + TypeScript

フロントエンドではcreate-react-appをejectせずに素のReact.jsを利用してSingle Page Application(SPA)を作成しています。加えて静的型付けを行うためにTypeScriptを利用しています。
また、WebでもAPIと同様に3つの各アプリケーションに加えて、共通のコンポーネントや共通ロジックを切り出すlib-webというローカルのnpm packageがあり、各アプリケーションがlib-webを参照する形になっています。

プロジェクトの参照関係(Web)

Emotion

Component Libraryというと少し大げさかもしれないですが、先に触れたように複数のアプリケーションがあるため、buttonやinputなどの最小の単位や一定のスタイルを持たせたdivなど、アプリケーションに依存しない再利用性の高い共通Componentを定義しています。アプリケーションを作成する際はこの共通Componentを組み合わせて作成しています。

スタイルはCSS-in-JSライブラリのEmotionを利用して、アプリケーション毎のスタイルをthemeProviderで切り替えたり、Component毎の個別のスタイルをjsxのcss propsで適用しています。
なお、社内向けのアプリケーションであるadminは共通コンポーネントは利用せず、MUIを利用しています。

テストについて

バックエンドほどではないですが、フロントでもJestを用いてテストを行っています。最低限、スナップショットを取得しておいて、意図しない変更が発生していないかを検知できるようにしています。
また、機能として重要な箇所や複雑な箇所については実際に特定要素をクリックした際に、意図したAPIが呼ばれるか、意図した動作をするかまで詳細にチェックしているところもあります。
加えて、コードフォーマットにはprettierを、コーディングルールの適用にはeslintを利用しています。eslintでは、例えばReact hooksのトップレベル以外やReact関数外からの呼び出しなどの不適切な利用を検出できるようにしています。

Pull Request時には以上を必須のテストとして、以下をマージの条件としています。

  • 1人以上からレビューでApproveをもらっている
  • バックエンドとフロントエンドのテストが全て通っている

Pull Request時のマージ条件

まとめ

今回は「Assured」の技術スタック(アプリ編)についてをお伝えさせていただきました。現在の技術スタックは現メンバーのスキルセットや開発体制、ビジネスの時間的な制約な上での最適解と思われるものを選択してきました。一方で、今後メンバーが増えた際にチームのスキルセットや開発体制が変化したり、フロント, バックエンドのエコシステムも変化していくかと思います。その際は、必要とあれば開発言語やフレームワークについても柔軟に変更していきたいと思っています。

今回の記事を通して「面白そう!」「自分ならこうするかも」などありましたら、「Assured」を通して世の中の変化を加速させていく仲間を絶賛募集中ですので、ぜひカジュアルにお話してみませんか?お待ちしております!
forms.gle