はじめに
niyase は2人チームで業務アプリを開発しています。勤怠・給与・会計・決算ができるアプリを、デスクトップ・クラウド・モバイルの3プラットフォームで提供しています。
少人数で開発していると、技術選定の1つ1つが重いです。「流行っているから」「みんな使っているから」では選べません。運用もデバッグも自分たちでやるので、理解できないものは入れられない。
この記事では、私たちが何を使っていて、なぜそれを選んだかを公開します。同じツールを使っている方には親和性を感じてもらえると思いますし、これから技術選定する方の参考になれば幸いです。
アーキテクチャ概要
Turborepo でモノレポを構成し、8つのアプリと5つの共有パッケージを管理しています。
アプリ
| アプリ | 技術 | 役割 |
|---|---|---|
| desktop | Electron + Electron Vite | デスクトップ版。オフラインでも動作 |
| cloud | Next.js 16 (App Router) | クラウド版(app.niyase.com)。チームでの共有・同期 |
| mobile | Expo SDK 54 (React Native) | モバイル版 |
| api | NestJS | メイン API サーバー |
| www | Next.js 16 | コーポレートサイト(www.niyase.com) |
| admin | Next.js 16 | 管理コンソール |
| estimate-pdf-generator | NestJS | 非同期 PDF 生成ワーカー |
| issue-estimator | NestJS + Anthropic SDK | AI 見積もりワーカー |
共有パッケージ
| パッケージ | 役割 |
|---|---|
| database | Drizzle ORM スキーマ・マイグレーション |
| ui | Radix UI + Tailwind CSS の共通コンポーネント |
| finance | 会計・XBRL ユーティリティ |
| eslint-config | 共有 ESLint ルール |
| typescript-config | 共有 TypeScript 設定 |
設計の軸はクラウドの便利さと、データ主権の両立です。クラウドでチーム間の共有・同期を便利に使いつつ、デスクトップ版ではオフラインでも全機能が動作します。ユーザーがクラウドに依存しなくても業務を続けられる選択肢を、技術で担保しています。
技術スタック一覧
言語・ランタイム
| 技術 | バージョン | 用途 |
|---|---|---|
| TypeScript | 5.7 〜 5.9 | 全レイヤーで統一 |
| Node.js | v24 (Docker) / v20 (CI) | サーバー・ビルド |
全レイヤーを TypeScript で統一しています。フロントもバックもスキーマ定義も同じ言語なので、型を共有パッケージ経由で使い回せます。2人チームでは「言語の切り替えコスト」がそのまま生産性に直結するので、統一の効果は大きいです。
モノレポ
| 技術 | 選定理由 |
|---|---|
| Turborepo | NX を検討したが壮大すぎた |
NX は機能が豊富ですが、設定量も多く学習コストが高いと感じました。TypeScript に寄せるなら Turborepo で十分という判断です。ただし、今後 AI によるコード生成の比重が増えれば、TypeScript 縛りの意味が薄れるので、NX が候補に入る可能性はあります。
フロントエンド
| 技術 | 用途 |
|---|---|
| Next.js 16 (App Router, Turbopack) | cloud / www / admin |
| Electron + Electron Vite | desktop |
| Expo SDK 54 (React Native) | mobile |
| React 19 | 全フロントエンドの基盤 |
3つのプラットフォーム(Web / デスクトップ / モバイル)を React ベースで揃えています。UI ライブラリやロジックの共有がしやすく、1つのプラットフォームで書いたコンポーネントを他に移植するコストが低い構成です。
バックエンド
| 技術 | 用途 |
|---|---|
| NestJS | API サーバー、非同期ワーカー |
| Swagger / OpenAPI | API ドキュメント自動生成 |
| Orval | OpenAPI → TypeScript クライアント + React Query hooks を自動生成 |
NestJS の Module / Controller / Service の構造が、2人チームでも秩序を保つのに効いています。API 定義から Orval でフロント用のクライアントコードを自動生成しているので、API とフロントの型ずれが起きません。
データベース・ORM
| 技術 | 用途 |
|---|---|
| PostgreSQL 17 | メイン DB(Central DB + WS DB の分離構成) |
| PGlite | デスクトップのローカル DB(WASM PostgreSQL) |
| SQLite (expo-sqlite) | モバイルのローカル DB |
| Drizzle ORM | 全 DB 共通の ORM |
なぜ Drizzle か。 Prisma は SQLite に十分対応していません。デスクトップではPGlite、モバイルでは SQLite を使うため、両方に対応できる Drizzle を選びました。SQL がそのまま見えるのでデバッグしやすく、マジックが少ない点も気に入っています。
なぜローカルDBを持つのか。 クラウドは便利ですが、クラウドにしかデータがない状態はリスクです。Google Workspace もエクセルダウンロードで離脱できる道を残しています。私たちも同じ考え方で、クラウドで便利に使ってもらいつつ、ユーザーがいつでもデータを手元に持てる設計にしています。
認証
| 技術 | 用途 |
|---|---|
| Firebase Auth | 全プラットフォーム共通の認証 |
| Firebase Admin SDK | API サーバーでのトークン検証 |
| Firebase Emulator | ローカル開発 |
なぜ Firebase か。 Google First の方針です。認証だけ Supabase、ストレージだけ AWS...と SaaS を分散させると、管理画面もドキュメントもバラバラになります。Google はかなりの領域をカバーしてくれるので、1つのエコシステムに寄せることで運用コストを下げています。管理画面の UI が使いやすいのも大きいです。
UI
| 技術 | 用途 |
|---|---|
| Tailwind CSS | スタイリング |
| Radix UI | ヘッドレス UI コンポーネント |
| Lucide React | アイコン |
| Sonner | 通知トースト |
| NativeWind | モバイル向け Tailwind |
| CVA + clsx | コンポーネントのバリアント管理 |
Radix UI はスタイルなしのヘッドレスコンポーネントなので、Tailwind CSS と組み合わせて自由にデザインできます。shadcn/ui のアプローチに近い構成です。
状態管理・API クライアント
| 技術 | 用途 |
|---|---|
| TanStack Query | サーバー状態管理 |
| Axios | HTTP クライアント |
| Orval | OpenAPI からの自動生成 |
Redux や Zustand のようなグローバル状態管理ライブラリは使っていません。サーバー状態は TanStack Query、UI ローカル状態は React の useState / useReducer で十分回っています。
決済・メール
| 技術 | 用途 |
|---|---|
| Stripe | サブスクリプション決済 |
| Resend | トランザクションメール |
どちらも開発者体験が良く、API がシンプルです。
クラウドインフラ
| 技術 | 用途 |
|---|---|
| Google Cloud Storage | ファイルストレージ |
| Google Cloud Pub/Sub | 非同期メッセージング(PDF生成、AI見積もり) |
| Terraform | インフラのコード管理 |
| Docker Compose | ローカル開発環境 |
Google First の方針に従い、クラウドは GCP に寄せています。ローカル開発では fake-gcs-server(GCS エミュレータ)と Pub/Sub エミュレータを Docker Compose で起動し、本番と同じ構成で開発できるようにしています。
AI
| 技術 | 用途 |
|---|---|
| Anthropic Claude (SDK) | AI 見積もり機能 |
issue-estimator というワーカーで、Pub/Sub 経由で受け取った案件データを Claude で分析し、見積もりを生成しています。
ドキュメント・PDF・OCR
| 技術 | 用途 |
|---|---|
| PDFKit / jsPDF | PDF 生成(サーバー / デスクトップ) |
| Tesseract.js | OCR(デスクトップ) |
| XLSX | Excel 入出力 |
業務アプリなので、帳票出力や書類読み取りの機能が必要です。これらはすべて JavaScript / TypeScript のライブラリで処理しており、外部サービスには依存していません。
テスト
| 技術 | 用途 |
|---|---|
| Vitest | フロントエンド・デスクトップのテスト |
| Jest | API サーバーのテスト |
| Testing Library | コンポーネントテスト |
| Supertest | API の E2E テスト |
CI/CD
| 技術 | 用途 |
|---|---|
| GitHub Actions | ビルド・テスト・リリース |
| Electron Builder | デスクトップアプリの署名・パッケージング(macOS / Windows / Linux) |
3つの選定基準
技術を選ぶとき、私たちは3つの軸で判断しています。
1. クラウドの便利さと、データ主権の両立
クラウドでチームの共有・同期を便利に使ってほしい。でも、クラウドにロックインされてデータが人質になる状態は作りたくない。この2つを両立させるのが、私たちの設計方針です。
PGlite(WASM PostgreSQL)をデスクトップに組み込むことで、オフラインでも全機能が動作します。クラウドは「便利な同期・共有の手段」であって、「なければ業務が止まるもの」ではありません。
Google Workspace もエクセルダウンロードで離脱できる道を残しています。私たちも同じ考え方で、クラウドを積極的に使ってもらいつつ、ユーザーがいつでもデータを持ち出せることを保証しています。
2. Google First
SaaS の種類は厳選して絞りたいと考えています。認証は Firebase、ストレージは GCS、メッセージングは Pub/Sub、インフラは Cloud Run — Google のエコシステムでかなりの領域をカバーできます。
サービスごとに別のベンダーを使うと、管理画面・ドキュメント・課金がバラバラになり、2人チームでは運用が回りません。Google の管理画面は UI が使いやすく、1箇所で多くのことを把握できるのも選定理由の1つです。
3. 自分でコードを書く前提で選ぶ
マジックが多いツールは避けています。問題が起きたとき、中身が見えないと2人チームでは詰みます。
Drizzle ORM は SQL がそのまま見えます。Turborepo は NX より設定が少なく、やっていることが把握できます。この「理解できる範囲に収める」というのが、少人数チームの生存戦略だと思っています。
ただし、AI によるコード生成の比重が増えてくると、この判断基準は変わるかもしれません。AI が設定の複雑さを吸収してくれるなら、NX のような高機能ツールを使う合理性が出てきます。技術選定は固定するものではなく、チームの状況に合わせて見直すものだと考えています。
AI 開発ツール
開発には Claude Code を全面的に使っています。コード生成だけでなく、設計判断の壁打ち・リファクタリング・テスト生成まで、開発プロセスのほぼ全域で活用しています。
私たちが特に注力しているのは、日本語ドキュメントを充実させて AI へのコンテキスト供給源にするというアプローチです。これについては別の記事で詳しく書く予定です。
まとめ
2人チームの技術選定は「少なく・深く・自分で理解できるもの」が基本です。
- クラウドの便利さ + データ主権 — 便利に使ってもらいつつ、ロックインしない
- Google First — SaaS を分散させず、1つのエコシステムに寄せて運用コストを下げる
- 理解できるものを選ぶ — マジックが少なく、問題が起きたとき自分で対処できること
このスタックに共感する方、あるいは「ここはこうした方がいい」という意見をお持ちの方がいれば、ぜひ話してみたいです。niyase では一緒に開発するメンバーを探しています。