仕組み¶
Taylis の中身が気になる管理者や開発者のための、設計の要約です。詳しい設計の文書(ほとんど日本語)は GitHub の docs/ にあります。
目指していること¶
- メッセージを確実に保存する
- 端末の間で確実に同期する
- 通知を確実に届ける(ただし、通知に頼らなくても正しく動く)
- 長い期間の履歴を、日本語と英語で探せる
- バックアップと復元がかんたん
- 余計な複雑さのない、保守しやすい構成
規模の目安は 数十人・数百の同時接続・数十万〜数百万件のメッセージ です。マイクロサービスや Kubernetes は使わず、 1 つのアプリ(モジュラーモノリス)で作っています。
全体の構成¶
flowchart TD
D["デスクトップ<br>Tauri + React"] --> C
W["ブラウザ<br>React"] --> C
I["iOS<br>SwiftUI"] --> C
A["Android<br>Compose"] --> C
C["Caddy<br>HTTPS・WebSocket"] --> APP["app<br>FastAPI・1 プロセス"]
APP --> DB[("PostgreSQL<br>+ PGroonga")]
APP --> S3[("versitygw<br>S3 互換")]
APP --> CV["converter<br>Gotenberg"]
APP -. "プッシュ" .-> P["APNs / FCM"] -. "通知" .-> PH["iPhone・Android"]
アプリはどれも Caddy を通して 1 つのサーバ(app)とやり取りします。データは PostgreSQL(メッセージ・検索の索引など)と versitygw(添付ファイルの中身)の 2 か所に置きます。スマートフォンへのプッシュは、サーバから APNs と FCM を経由して届きます。
サーバの中では、API・WebSocket・送信待ちの表(outbox)の処理・プッシュの送信が 1 つのプロセスで動いています。
flowchart TD
API["REST API"] -- "同じトランザクション" --> DB[("PostgreSQL<br>データ + outbox")]
DB -- "outbox を読む" --> R["OutboxRelay"]
R --> BUS["EventBus"] --> HUB["RealtimeHub"] -- "WebSocket" --> CL["アプリ"]
R --> PL["PushPlanner"] --> PS["PushSender"] --> P["APNs / FCM"]
| 部分 | 技術 |
|---|---|
| サーバ | Python、FastAPI、SQLAlchemy、Alembic(移行) |
| データベース | PostgreSQL 17 と PGroonga(全文検索) |
| 添付ファイル | S3 互換のオブジェクトストレージ(既定は versitygw)。BlobStore の抽象の後ろにあり、ほかの S3 互換のものに替えられます |
| リアルタイム | WebSocket。イベントの配り方は EventBus の抽象の後ろにあり、今はメモリの中の実装です(横に増やすときは Redis などに替えられます) |
| 通知 | APNs(iOS)、FCM(Android)。PushProvider の抽象の後ろ |
| デスクトップ / ブラウザ | Tauri 2、React、TypeScript(同じ画面をブラウザ版としても配信) |
| iOS | Swift、SwiftUI、Swift Concurrency |
| Android | Kotlin、Jetpack Compose、Coroutines / Flow |
どのアプリも、同じ API の仕様(openapi/openapi.json) でサーバとやり取りします。アプリごとに違う動きを作らないようにしています。
使わないもの¶
Redis、Kafka、RabbitMQ、Elasticsearch、Kubernetes、分散データベースは使いません。この規模では PostgreSQL 1 台で 十分で、部品が少ないほど壊れにくく、バックアップも簡単だからです。将来、横に増やす必要が出たときに替えられるように、 境目(EventBus、BlobStore、PushProvider)だけを決めてあります。