綴Photo の勉強ノートに戻る
開発記録2026.06.16

テンプレを磨く前に「管を通す」を選んだ — 3つの器と2本の管

設計アーキテクチャ綴Photo

綴Photo を mobile / backend / web に分けて作ってきて、「次に何をやるか」で構造づくりを優先した判断の記録です。

ここで言う「web」は、綴ったものを web でも見られるように開発中のアプリ(紡Book)のことです。

なぜ「テンプレ磨き」より「管を通す」を先にしたか

綴Photo には器が3つあります。スマホで綴る mobile、データを受けて貯める backend、それを美しく見せる web。器はそれぞれ用意できていたのに、間が繋がっていなかった。だから web は、自分で用意したダミーデータ(モック)で動いていて、スマホで実際に綴ったものは流れてきません。

この状態で web の見せ方(テンプレ)を増やしても、中身は架空のサンプルのまま=「使っていない機能のカタログ」になります。自分が日常的に綴ったものが web に出てこないと、自分が一番のユーザーになれません。だから、見栄えを足すより先に、スマホで綴ったものが web で見えるところまで管を1本通すことを選んだ。細くていいから一周させる方が、カタログを並べるより効く、という判断。

3つの器と2本の管

通してみて、全体はこういう形だと分かった。

器は3つ。管は2本。残る場所は端末 SQLite と PostgreSQL だけである。mobile Flutter端末 SQLitePhotos・Meaningsclient_photo_idsync_state綴る。閉じても残る① POSTbackend DjangoPostgreSQL 1個Photo・Meaning同じ列 client_photo_id同じ列 sync_stateFavoriteMemory を web が読む閉じても残る② GETweb Next.js自前の DB は無い画面のメモリGET /api/favorites/読むだけ。保存しない① は POST /api/sync/memories/。② は GET /api/favorites/。web は真ん中の Postgres を読む。別の棚は無い。列の仕切りは左と真ん中で揃えてある。

この列の揃えを、スキーマがミラー と呼んでいます。

両端の叩き方

管の両端は、向きが違うだけで作りはほぼ対称だった。どちらも「APIを叩く+Bearerトークンで認証+JSONでやりとり」。

送る側(mobile・POST):

dart
// 認証ヘッダを付けて JSON を POST する(http パッケージ)
Map<String, String> _headers(String token) {
  final headers = {'Content-Type': 'application/json; charset=utf-8'};
  if (token.isNotEmpty) headers['Authorization'] = 'Bearer $token'; // ← 認証
  return headers;
}
// SyncService が綴りを整形して POST /api/sync/memories/ に送る
// buildSyncItems が「mobile の形 → backend の形」に変換する継ぎ目を担う

取る側(web・GET):

ts
// 同じく Bearer トークンを付けて取得し、純関数(同じ入力なら必ず同じ結果を返す変換役)で web の形に変換する
const rows = await apiRequestJson(`${API_BASE}/api/favorites/`, { headers: devAuthHeaders() });
return rows.map(mapFavoriteMemory); // ← サーバの行を web 表示用に整形する「継ぎ目」

ひとつ断っておくと、今このトークンは 開発用に手で発行した長命のもの を差しているだけで、本番のログイン認証ではない(ログイン機能は後で作る)。あくまで localhost で一周を確かめるための足場で、トークンの実物はもちろん公開しない。

両端には必ず「変換」がいる

器ごとにデータの形が違います。だから管の両端には、形を合わせる変換が1枚ずつ要ります。

中身は表に残る。変換は交差で、置き場所ではない。端末 SQLitePhotos・Meanings綴りの実体。閉じても残るPOST JSONPostgreSQLPhoto・MeaningFavoriteMemory。閉じても残るGET JSON画面のメモリ表示用の形保存しない交差buildSyncItemsJSON はメモリ。保存しない交差mapFavoriteMemory純関数。保存しない器の中身は Postgres が1箇所で持つ。形の変換は両端にバラけている。

この「継ぎ目(変換)」が、実は一番つまずいた場所だった。画像が無い綴りで空文字を <img> に渡してしまった件も、プレビューが古い索引からデータを引き直していて空になった件も、どちらもこの両端の変換のところで起きています(それぞれ別の記事に書いた)。構造として見ると、器の中身ではなく 継ぎ目に注意がいる、というのが管を通して一番効いた学びだった。


この記事は2026年6月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。