結果だけ出されても、信用できない。 だから wa/ri は、計算過程をすべて見せます。
1. 概要
wa/ri(わり)は、旅行・飲み会・パーティ・シェアハウスなどグループでの支払いを、無料・登録なしで気軽に管理・精算できる割り勘アプリです。
「誰が誰にいくら払うか」という結果だけでなく、その計算過程までユーザーに見せることをコンセプトに設計しています。
iOS / Android / Web / LINE(LIFF) の4プラットフォームに対応しています。
2. 開発の動機
割り勘アプリは便利なはずなのに、使うたびに引っかかることが多くありました。全員の名前を登録しないと始まらない。LINE連携には相手の協力が必要。無料枠が狭くてすぐ課金を求められる。広告が多くて、肝心の記録が見えづらい。そして極めつけは「この計算、本当に合っているの?」という不信感です。結果だけをまとめて提示されても、なかなか納得できません。
こうした”割り勘のしんどさ”をすべて取り除きたくて、wa/riを作りました。
最初はiOS版(Swift)として一人で使うつもりで開発を始めましたが、Android勢の友達から「iOSだけだと使えない」という声をもらい、それに応える形でAndroid / Web版(Flutter)と、LINE上で動くLIFF版へと展開していきました。友達の不満から生まれ、友達の声で広がってきたアプリ——それがwa/riの出発点です。
3. システム構成

- iOS:Swiftによるネイティブ実装。Liquid Glassに沿ったiOSらしいUI
- Android:FlutterによるMaterial Design準拠の実装
- Web:FlutterによるMaterial Design準拠の実装。ビルド成果物をnginx入りのコンテナとして静的配信
- LINE(LIFF):上記のWebアプリをLIFF(LINE Front-end Framework)経由でLINE内から起動。専用の別実装ではなく、Webクライアントをそのまま再利用しています
クロスプラットフォーム一本化での工数最適化よりも、各OSユーザーが慣れ親しんだUIを届けることを優先しています。
バックエンド
- ホスト:ConoHa VPS(Ubuntu)
- ドメイン管理 / DNS:Squarespace Domains(旧 Google Domains から移管)
- API:NestJS
- ORM / DB:Prisma + PostgreSQL
- コンテナ:Docker(API + DB を docker-compose で管理)
- リバースプロキシ:ホスト側に nginx
- HTTPS:Let’s Encrypt(無料証明書)
- コンテナ管理:Portainer
- バックアップ:1日1回、VPN経由で自宅NASへオフサイト転送
データ設計の肝
- アカウントとルームメイトの完全分離
- 「アカウント」(Y.netlaboアカウント、LINE連携可)は記録の管理者
- 「ルームメイト」は記録上の登場人物。事前登録もクイックスタート機能による自動仮登録も可能
- 参加者全員にアカウントを強制せず、管理者ひとりで割り勘が成立する設計
- 招待URLによる共同編集:UUID形式の招待URLを正規ルートで起動すると、他のアカウントとも同じルームを共同編集可能。リンクは再発行・無効化にも対応
- 3段階の権限:オーナー / 編集可 / 閲覧のみ
- 認証・認可:自前のJWTベース認証。リクエストヘッダーのトークンとユーザーID、レコードの所有者・招待紐付けで認可を制御。LINEログインおよびSign in with Appleによる外部認証連携にも対応しているが、これらは既存のY.netlaboアカウントへのuid付与として扱う設計のため、LINEやAppleに依存せず、自前認証だけでも成立する
4. wa/riの核:計算過程の見える化
wa/riの最大の特徴は、計算結果だけではなく、その計算過程までユーザーに見せることにあります。
簿記の発想に基づくデータ構造
開発者の商業高校時代のバックグラウンドから、wa/riの取引データは簿記の借方/貸方で記録する構造を採用しています。すべての取引が会計記録のように積み上がるため、結果だけでなく記録そのものを提示できる——これが「過程を見せられる」根本理由です。
段階的な相殺ロジック
最終的な精算は、ルールベースで段階的に相殺を進めるアプローチで求めます。
- Phase 1:立替トランザクションをグループ化し、借方/貸方を積み上げ
- Phase 2:集金(返済済み)トランザクションを反映
- STEP 1(自分同士):自分が自分の分まで立て替えた額を相殺
- STEP 2(二者間相殺):AとBが互いに立て替えていれば、小さい方を相殺
- STEP 3(債権譲渡):3人以上の関係で「中間に挟まる人」を飛ばし、送金回数を実質的に減らす
派手なアルゴリズム最適化ではなく、簿記の発想に沿った段階的な処理。だからこそ各ステップが人間に説明可能になっています。
計算と同時に履歴を積む
各ステップの処理時に、人間が読める形の説明文(例:「○○が立替: ¥1,000」「自分の支払い分を相殺: ¥250」「○○ との貸し借りを相殺: ¥500」)を tradeHistory という配列に逐次pushしています。計算結果を出してから後で説明を生成しているのではなく、計算しながら同時に履歴を積み上げているため、過程と結果が絶対にズレません。
3モードの表示トグル
UIには3つの独立したトグルがあり、ユーザー自身が見たい情報量を選べます。
- 詳細表示:全履歴をステップごとに表示
- まとめる:同じ相手への複数立替などを集約して表示
- 0円消し:相殺済み(0円)のエントリを非表示
「割りすぎない」、つまり過剰に細かく見せすぎたり、逆に削りすぎたりしない適切な粒度を、ユーザー自身が選べるように設計しています。相殺済みエントリも、検証したい人には残し、結果だけ確認したい人には隠せる——この柔軟さも開発初期から組み込んでいます。
設計思想:情報は全部持つ/見せ方は可変
wa/riは「結果しか出さないから信用できない」という不満への回答として、情報は全部保持し、見せ方だけユーザー側で可変にするという方針で設計しています。これはデータ構造(借方/貸方の帳簿)、計算ロジック(tradeHistoryの逐次記録)、UI(3モードトグル)まで、アプリ全体を貫く一貫した思想です。
5. 工夫した点・苦労した点
iOSをSwiftで残した理由
Web / AndroidはFlutterで実装していますが、iOSのみSwiftネイティブのまま残しています。クロスプラットフォーム一本化で工数を削減する選択肢もありましたが、wa/riではiOSユーザーにはLiquid Glass、AndroidユーザーにはMaterial Designと、各OSユーザーが慣れたUIを届けることを優先しました。プロダクトの「気軽さ」を、構造(連携不要)だけでなく、見た目の文化的整合性でも貫きたかった判断です。
Flutter移植のリアル(AI活用①)
iOSプロジェクトを起点に、Copilotを使ってFlutterへ移植しました。ロジック面は比較的スムーズに進みましたが、UI周りの初期アウトプットは「ラベルとボタンがただ並んでいるだけ」の、デザインが欠落した状態でした。
打開策として、テキスト指示だけでなくiOS側の画面スクリーンショットをそのままAIへの入力として併用しました。視覚情報を加えることでレイアウトやコンポーネント選択の精度が大きく改善し、実用レベルのUIに到達できました。
AIにUIを任せる際は、コードと自然言語だけでなく参照画像を併用するのが効く。
この経験は、その後の継続改修でも活きています。
計算ロジックの検証進化(AI活用②)
開発初期は、テストケースを手で組み、期待値と実行結果を目視で照合する地道な検証を繰り返していました。現在は計算ロジックをモジュールとして独立させ、Claudeにテストコードを生成させる形に進化しています。手計算 → ロジック分離 → AI生成テストへと、検証フロー自体が育ってきました。
フロントエンドで計算させない設計
wa/riではフロントエンド側で極力計算をさせない設計を取っています。計算ロジックがバックエンドに集約されているため、
- バックエンドのテストだけで全クライアント(iOS / Android / Web / LIFF)の正しさを担保できる
- 同じ計算を4つのクライアントに重複実装せずに済む
- 修正もサーバー更新だけで全クライアントに即反映され、アプリストア審査やビルド待ちを回避できる
マルチプラットフォーム個人開発において、この「ロジック集約・テスト容易性」設計は実運用で大きな効果を発揮しています。
6. 今後の展望
- 事前集金パターンへの対応:現在の wa/ri は「その都度立て替え→最終精算」のフローを前提としています。一方で、旅行や合宿でよくある「事前に集金してプールし、現地で運用、最後に精算」というパターンに未対応のため、API側から実装を進めています。実際の合計が事前集金額を下回った場合の返金処理もあわせて対応予定です。
- 多通貨対応:海外旅行など、複数通貨が混在する精算シーンへの対応を予定しています。
7. 使った技術
フロントエンド
- Swift(iOS / Liquid Glass)
- Flutter(Android / Web / Material Design)
- LIFF(LINE Front-end Framework)
バックエンド
- NestJS
- Prisma
- PostgreSQL
インフラ・運用
- ConoHa VPS(Ubuntu)
- Squarespace Domains(DNS / 旧 Google Domains)
- Docker / docker-compose
- nginx(リバースプロキシ+静的配信)
- Let’s Encrypt
- Portainer
開発支援
- GitHub Copilot(移植フェーズ)
- Claude(テストコード生成・継続改修)

No responses yet