こんにちは!スタッフエンジニアの @kenkoooo です。先日、賃貸住宅のデータサービス「estie レジリサーチ」の提供を開始しました。
estie レジリサーチとは、賃貸住宅物件の取得や運用の意思決定をサポートするためのツールで、データ収集や分析を高速に行えることをウリにしています。アプリの技術選定もこれに沿ったものになっています。サービス全体は、ウェブアプリ・査定サービス・データパイプラインといった要素からなりますが、この記事ではウェブアプリ側で採用されている技術を見ていきます。
Rust によるバックエンド
estie レジリサーチはデータが肝になるアプリなので、データの情報量をアプリ内で落としてしまわないためにも、静的型付け言語を使いたいというモチベーションがあります。
静的型付け言語としてサーバーサイドでよく採用されるものとしては、Java や Kotlin などの JVM 系、Go 言語、Rust などが挙げられます。レジリサーチでは、静的型付け言語であることに加え、以下の観点から Rust をバックエンドに採用することにしました。
- 表現力の高い trait などの言語機能によって、効率的にコーディングができること
- workspace などの機能を使うことで、モジュール間を粗結合にでき、コードベースが大規模になっても複雑になってしまうことを防げること
- rust-analyzer などの周辺ツールが強力で、高い開発生産性を実現できそうだということ
- 所有権の制約を受けることで、内部に状態を持たせてしまうようなミスをコンパイル時に防げること
GraphQL フレームワーク: async-graphql
Rust バックエンドの API として GraphQL を採用しています。Rust の GraphQL フレームワークとしては async-graphql や juniper などが有名ですが、dataloader が同梱されていることや、 @oneOf
が先行実装されていることなどから、async-graphql を採用しました。
SQL ライブラリ: sea-orm
バックエンドは MySQL をデータベースとして使っていて、MySQL への接続には sea-orm を使っています。Rust の ORM としては diesel が老舗ですが、非同期処理に対応していることから sea-orm に決めました。
sea-orm は実行時に接続先のデータベースを差し替えることができるので、本番稼働時は MySQL に接続していますが、テスト時にはオンメモリの SQLite を使っています。MySQL 向けに書いたクエリが SQLite で動かないことがあるというつらさはあるのですが、テストを高速に並列実行できる快適さには代えられず、この方式で頑張っています。
sea-orm は生の SQL 文を書くこともできるので、ORM でカバーされていないクエリは生クエリで対応することもできますが、自前の関数を定義して ORM 風に利用することもできます。以下の例では ST_Distance_Sphere(POINT(longitude, latitude), POINT(longitude, latitude))
を実装しています。
// 自前の関数を定義 struct Point; impl Iden for Point { fn unquoted(&self, s: &mut dyn std::fmt::Write) { write!(s, "POINT").unwrap() } } struct StDistanceSphere; impl Iden for StDistanceSphere { fn unquoted(&self, s: &mut dyn std::fmt::Write) { write!(s, "ST_Distance_Sphere").unwrap() } } // 自前の関数を利用する Expr::expr( Func::cust(StDistanceSphere) .arg(Func::cust(Point).arg(Column::Longitude).arg(Column::Latitude)) .arg(Func::cust(Point).arg(Column::Longitude).arg(Column::Latitude)) ) .lt(distance)
React + Next.js + TypeScript によるフロントエンド
estie 社内では React リテラシーが高く、エンジニアだけでなくデザイナーも React を書いています。社内ライブラリとして UI コンポーネントが React で提供されているなど、サポートも手厚いので、フロントエンドの実装には React + TypeScript を採用しました。レジリサーチでは分析機能に力を入れていれていることもあり、ロジックと UI をシームレスにつなぐことができる React を採用するのは自然な判断でした。
フレームワーク: Next.js
React のフレームワークとしては Next.js を採用しています。B2B サービスという特性上、サーバーサイドレンダリングをフルに活用しているわけでありませんが、zero-config で必要十分な機能が揃っていることや、ユーザー認証基盤として使っている Auth0 との相性の良さから Next.js を利用しています。
Rust のバックエンドがあるので Next.js の Server-side endpoint はあまり多用していませんが、Feature toggle など「API キーは公開したくないが、フロントエンドで完結させたい」といったケースでは Next.js 側に実装しています。
最後に
レジリサーチに限らず、estie ではプロダクトのユースケースに適した技術を選定するだけでなく、開発スピードを非常に重視しています。快適な開発環境で、プロダクトに次々と新しい機能を実装していきたい方は、是非ご入社ください!