glucose flightを支える技術スタック - Full TypeScriptでの開発
はじめに
私が所属している株式会社ザ・ファージは、血糖管理を支援するヘルスケアプラットフォーム「glucose flight(グルコースフライト®︎)」を開発・提供している会社です。
本記事の目的
本記事を公開する目的は、弊社について知っていただくことと、Full TypeScriptでの開発体制について共有することです。
弊社では、TypeScriptという共通言語を通じて、フロントエンド・バックエンド・モバイル・インフラの境界を設けず、一人のエンジニアがプロダクト全体を見られる体制を構築しています。このアプローチにより、エンジニアは職種の垣根を超えてプロダクト全体を考えられる人材へと成長できる環境となっています。
血糖管理支援サービス「glucose flight」について
「glucose flight」は、血糖変動から食事を評価し、食事管理を支援するサービスです。人々の生活データを可視化し、医師との情報共有を円滑にします。これにより、患者は効率的に血糖管理に取り組め、医師は個々の患者に適した生活提案が提供しやすくなります。
技術スタック
選定基準
弊社では、以下の観点を重視して技術を選定しています:
- 開発者体験の向上: 明確なアーキテクチャと依存関係により、コードの可読性と保守性を担保。
- スケーラビリティ: 機能ごとにモジュール化し、必要に応じて分割・拡張可能な設計。
- 実績とコミュニティ: 活発に開発されており、エコシステムが充実している技術を優先。
- 言語の統一によるスイッチングコストの削減: Full TypeScriptにすることで、フロントエンド・バックエンド間の技術スタック切り替えコストを最小化し、エンジニアの生産性を向上。
また、上記の通り、フロントエンドエンジニア・モバイルエンジニア・バックエンドエンジニア・インフラエンジニアといった分業制ではなく、全員「エンジニア」として、プロダクトを一貫して管理できることを重視しています。
全般
全方位TypeScriptで構築しており、リポジトリはフロントエンドとバックエンドでそれぞれモノレポ構成です。
フロントエンド:
glucose-flight-frontend/
├── apps/ # アプリケーション層
│ ├── glucose-flight-app/ # 患者向けアプリ(PWA + Tauri V2)
│ ├── glucose-flight-dashboard/ # 医療従事者向けダッシュボード
│ ├── glucose-flight-dietitian/ # 管理栄養士向けダッシュボード
│ ├── glucose-flight-ops/ # 運用管理者向けツール
│ ├── glucose-flight-report/ # レポート生成アプリケーション
│ └── glucose-flight-sponsored-trial/ # スポンサー向けダッシュボード
└── packages/ # 共通ライブラリ層
├── glucose-flight-ui/ # UIコンポーネントライブラリ
├── glucose-flight-swr/ # API通信Hooksライブラリ
├── glucose-flight-core/ # 共通ビジネスロジック
└── glucose-flight-rrv7/ # React Router V7ユーティリティ
...
バックエンド:
glucose-flight-backend/
├── packages/
│ ├── backend/ # NestJS バックエンドAPI
│ ├── database/ # PostgreSQLデータベース & マイグレーション
│ └── typescript-client/ # 自動生成されたTypeScriptクライアント
└── docker-compose.yml
APIの型の共有は、バックエンドのOpenAPI仕様から生成したTypeScriptクライアントパッケージをnpmの非公開パッケージとして公開し、フロントエンドリポジトリで使用しています。これにより、フロントエンドとバックエンドで型の一貫性が保たれています。
パッケージマネージャは、フロントエンド・バックエンドともにnpm、npm workspacesを採用しています。
フロントエンド
React Router V7 + Vite + Emotion + Radix UI + SWR を主要なコンポーネントとして採用しています。
採用した技術
- React: 現代のフロントエンドのデファクト。
- Vite: 高速なビルドツールのため、開発者体験が向上。
- React Router V7: フレームワークとして使用。純粋なSPAフレームワークがほしかった。ここでは詳細は書かないが、Next.js app routerだと純粋なSPAを実現するのはほぼ不可能。パスパラメータなど一部機能で制約がかかる。
- Emotion: CSS-in-JSによる動的なスタイリングとテーマ管理。Tailwindを採用しなかった理由は、破壊的変更への追従コストと、純粋なCSS記法で書けるライブラリを望んでいたため。
- Radix UI: スタイルを持たないヘッドレスUIコンポーネント。高品質なUI基盤を提供し、Emotionと組み合わせてカスタマイズ可能なコンポーネントを構築したかったため。
- SWR: データフェッチとキャッシュ戦略の王道。フロントエンドは基本的にSPAのため、効率的なデータフェッチを行うために採用。
- Turbo: モノレポのビルドとテストを高速に管理するため。
モノレポ構成の理由
複数のアプリケーション(患者向けアプリ、医療従事者向けダッシュボード、管理栄養士向けダッシュボードなど)を効率的に管理するため、モノレポ構成を採用しました。
また、共通のUIコンポーネントライブラリ(glucose-flight-ui)やAPI通信ライブラリ(glucose-flight-swr)を各アプリケーションで共有することで、開発効率と一貫性のある開発を実現しています。
バックエンド
NestJS + Prisma + BullMQ を主要なコンポーネントとして採用しています。
NestJSを採用した理由
採用当時、NestJSはTypeScriptのバックエンドフレームワークの中で最も高機能であったため採用しました。
具体的には、以下のメリットがあります:
- モジュールシステムによる明確な依存関係: 依存関係を明示的に管理でき、循環依存が検出可能。
-
OpenAPI(Swagger)の自動生成:
@nestjs/swaggerにより、コードからOpenAPI仕様を自動生成し、ドキュメントとコードの乖離を防止。
バックエンドの設計
バックエンドではレイヤードアーキテクチャを採用しており、明確な依存関係のルールを定めています。
src/
├── routes/ # エンドポイント定義層(Controller)
├── features/ # アプリケーション機能層(Service)
├── domains/ # 業務ロジック層(Domain Service)
├── shared/ # 共通基盤層(PrismaService、S3Service等)
└── batches/ # バッチ処理
各レイヤーの依存関係は以下の通りです:
各レイヤーは明確な責務を持ち、上位レイヤーは下位レイヤーに依存できますが、下位レイヤーは上位レイヤーに依存しません。
また、ESLintで依存関係のルールを強制しており、誤った依存を静的に検出できるようにしています。
主要ライブラリ
-
Prisma:
schema.prismaの定義により、TypeScriptの型とデータベーススキーマを一元管理。 - BullMQ: Redisベースの高性能なジョブキュー。ジョブキューをインフラ側に依存させず、アプリケーション層で制御できるライブラリを選定。
- Passport: JWT戦略による認証機能。Auth.jsやBetterAuthのような多機能なライブラリではなく、認証フローを細かく制御できるシンプルなライブラリを選定。
データベース
PostgreSQLを採用しています。
PostgreSQLを選定した理由
1. MySQLと比較した書き込み性能の優位性
- MVCC(Multi-Version Concurrency Control): 複数バージョンの行を保持することで、読み取りと書き込みが互いにブロックしない。
- WAL(Write-Ahead Logging): トランザクションログを先に書き込むことで、高速かつ安全な書き込みを実現。
- 大量書き込み時の優位性: 本システムでは血糖値データ、食事記録、ヘルスデータなど、大量の時系列データを高頻度で書き込むため、PostgreSQLの書き込み性能が重要。
2. 2038年問題への対応
MySQLのTIMESTAMP型は2038年問題(2038年1月19日以降を表現できない)を抱えていますが、PostgreSQLのTIMESTAMPTZ型は-4713年から294276年までの範囲をサポートしており、長期的なシステム運用が可能です。
3. Prismaとの相性
Prismaで十分にサポートされているデータベースであり、@db.Timestamptz(6)など、PostgreSQL固有の型を活用できます。
インフラ
フロントエンド
Vercel を採用しています。
SPAで運用しているため、S3 + CloudFrontでも実現可能ですが、一部の旧プロダクトでNext.jsを使用していることから、余計なことを考えなくて良いVercelを選定しました。将来的にはAWSへの完全統一も検討しています。
バックエンド
AWS ECS + Fargate、Redis、RDS などを採用しています。
弊社には「インフラエンジニア」が在籍していないため、アプリケーション出身のエンジニアでも理解しやすいシンプルな構成を重視しています。コンテナベースでサーバーレスなアーキテクチャを選定することで、インフラの運用負荷を軽減しています。
現在はCloudFormationのYAMLファイルを順番に流していく形で管理していますが、将来的にはAWS CDKやPulumiを使ったFull TypeScriptのインフラ構築を検討しています。
モバイル
現在リリースしているバージョンではFlutterを採用していますが、以下の課題があるため、Tauri V2への移行を進めています:
- Flutter専門の業務委託での運用がメインとなっており、一人のエンジニアがプロダクト全体を見られる体制と反している。
- Webで作成した共通のUIコンポーネントが使用できず、UI/UXの統一感が失われてきている。
Tauri v2を選定した理由
- コードベースの共通化: Web(React)のコードをそのままiOS/Androidアプリとして利用可能。
- クロスプラットフォーム対応: Web、Android、iOS、Mac、Windowsの全プラットフォームに対応。
- Over-the-Air Update(OTA): アプリストアの審査を経ずに、アプリの資材を更新できる。
- 小さいバイナリサイズ: Electronよりもサイズが小さく、セキュア。
- PWAとの共存: Progressive Web Appとしても動作し、段階的な移行が可能。
- ネイティブ機能へのアクセス: Rustで実装されたネイティブ機能を呼び出し可能。
近い将来、TypeScript(一部Rustによるネイティブコードを含む)で、社内のTypeScriptに精通したエンジニアのみでモバイルアプリの開発・運用を行っていく予定です。
最後に

株式会社ザ・ファージでは、Full TypeScriptでの開発により、フロントエンド・モバイル・バックエンド・インフラの垣根を超えてプロダクト全体を見られるエンジニアを募集しています。
私たちが目指すもの
日本には約1,000万人の糖尿病患者がいると言われており、適切な血糖管理は患者の生活の質を大きく左右します。しかし、従来の医療現場では、患者と医療従事者のコミュニケーションは限られた診察時間に制約され、日々の生活習慣の改善をサポートすることが困難でした。
私たちは、テクノロジーの力でこのような課題を解決し、患者が自分の健康をより良くコントロールできる世界を実現しようとしています。
このような方と一緒に働きたい
- 技術で課題を解決することに情熱がある方
- フロントエンドもモバイルもバックエンドも書ける、あるいは書けるようになりたい方
- TypeScriptが好きで、Full TypeScript環境で開発したい方
- アーキテクチャ設計やプロダクト全体の技術的意思決定に関わりたい方
- スタートアップの環境で、裁量を持って開発に取り組みたい方
一緒に働く魅力
- プロダクト全体を見渡せる:特定の領域に閉じず、フロントエンドからインフラまで一貫して開発できます。
- 成長できる環境:医療ドメイン、LLM、大規模データ処理など、多様な技術領域に触れられます。
- 社会的意義のあるプロダクト:患者や医療従事者から直接フィードバックを受け、プロダクトの価値を実感できます。
少しでも興味を持っていただけた方へ
弊社ではカジュアル面談を随時受け付けています。
技術スタックやプロダクトについて、もっと詳しく知りたい方は、弊社のホームページからお気軽にご連絡ください。
一緒に働けることを楽しみにしています!
Discussion