本文へ移動
松葉 / Matsuba ・ 開発過程資料

採用技術/ Tech Stack

使っている技術と、それぞれが担っている役割

新しく持ち込んだ技術はほとんどありません。松葉の既存の土台に、長期の保管と、数えて気づく仕組みに関わる部分だけを足しています。ここでは分類ごとに、何を使い、それが何を担っているかを並べます。最後に、記録の仕組みと検知の仕組みで共通にした部分・あえて分けた部分を整理しました。

01

画面Frontend

Next.jsApp Router

Box のアクティビティタブと、全社の管理画面。アラートの運用画面もここに含まれます。管理画面は既定で検索エンジンに載せない設定です。

ReactUI

タイムラインの日付グループ、カードの一覧、詳細のウィンドウ。表示の判断(どのボタンを出すか、どの空状態を出すか)は純粋な関数に切り出し、画面はそれを描くだけにしています。

TypeScriptTypes

アラート 29 種の対応表(表示名・アイコン・系統・推奨アクション)は、種別を漏らすと型が合わずコンパイルで落ちます。更新の漏れが本番まで届きません。

Tailwind CSSStyling

色・角丸・余白をトークンとして共有します。重大度や状態の色も直接の色指定ではなくトークン経由で、画面ごとに割れないようにしています。

shadcn/uiComponents

ダイアログ・入力欄・バッジ・表の土台。松葉のトークンを当てて使っています。

Radix UIPrimitives

キーボード操作と読み上げ対応を持つ土台部品。詳細のウィンドウでは、焦点の閉じ込め・Esc で閉じる・閉じたときに元のカードへ戻す、をここが担います。

TanStack QueryData

30 秒ごとの自動更新、ページを切り替えている間の前ページ保持、操作後に必要な範囲だけの取り直し。回線が切れたときは失敗ではなく待機に入るため、「最終更新: n 秒前」を常設して劣化に気づけるようにしています。

nuqs

nuqsURL State

絞り込み・ページ・表示件数を URL に持たせます。「この条件の一覧を見てほしい」を URL のまま渡せます。

ZodValidation

入出力の検証。同じ定義を画面と API の両方で使います。理由の文字数(10〜256)のような規則も 1 か所で決めます。

02

API とデータAPI & Data

HonoHTTP

API の土台。全社の監査ログ 8 端点、アラートの運用 7 端点、配送運用 8 端点。いずれもログイン確認とシステム管理者確認の二段の保護を通します。

Node.jsRuntime

API と定期処理の実行環境。5 分ごとの検知も、月次の退避も、同じ実行ファイルから引数で振り分けて起動します。

PostgreSQLDatabase

記録も、アラートも、取り出しの台帳もここに入ります。読み書きの範囲をデータベース自身が持つため、アプリケーションに穴があっても範囲外は返りません。

SupabaseManaged Postgres

データベースの運用基盤。範囲制限の規則も拡張機能の設定も、版管理された手順として適用します。

PrismaORM

スキーマ定義とマイグレーション。月ごとの分割・条件つきの索引・範囲制限は表現できないため、その部分は手書きの SQL を末尾に足す形で運用しています。

RLS

Row Level Security行単位のアクセス制御

行ごとに「誰が読めるか・書けるか」をデータベース側で決める仕組み。記録は追加は全員/参照は管理者のみ/更新と削除は規則そのものが無い形にしてあります。

SECURITY
DEFINER

SECURITY DEFINERElevated Function

定義した人の権限で動く関数。Box のメンバーはこの関数だけを呼べます。閲覧者を引数に取らず接続情報から取るので、他人になりすまして呼ぶことができません。

Keyset

cursor ページングKeyset Pagination

「何件目から」ではなく「この行の次から」で続きを取る方式。記録は(発生時刻, 連番)の複合キーを印にするので、読んでいる最中に行が増えても重複や取りこぼしが起きません。

03

長期保管と取り出しStorage & Export

pg_
partman

pg_partmanPartition Manager

月ごとの子テーブルを自動で作り、古いものを切り離す拡張機能。数ヶ月先まで先に作り、25 ヶ月を超えた月を別のスキーマへ移します。切り離すだけで、消しはしません。

pg_cron

pg_cronIn-Database Scheduler

データベースの中で定期処理を回す拡張機能。登録は1 本だけで、週次で子テーブルの整備が走ります。

COPY

COPY ... TO STDOUTStreaming Export

データベース自身に CSV を組み立てさせ、結果をそのまま流す命令。アプリケーションが全行をメモリに載せません。10 万行でも 100 万行でも使うメモリは変わりません。

gzip
crc32c

gzip / crc32cCompression & Checksum

圧縮と、壊れていないことの検査値。退避では行数と検査値の両方を突き合わせてから、元の月を捨てます。

Cloud StorageBuckets

1 つのバケットを 2 つの用途で使い分けます。取り出した CSV の置き場と、退避した月の置き場です。既定は Archive クラス(安く長く置く)で、CSV の置き場だけ Standard クラスを指定しています。

Life
cycle

オブジェクトライフサイクルRetention Rules

置き場ごとの寿命の設定。取り出した CSV は7 日で自動的に消え、退避した月は無期限で残ります。消す処理をアプリケーション側に持たせません。

Signed
URL

署名付き URL(V4)Signed URL

期限つきで 1 つのファイルだけを取れる URL。取り出した CSV は 24 時間、退避した月はその場で 15 分だけ発行します。API はファイルの中身を通しません。

RFC4180
BOM

RFC4180 / UTF-8 BOMCSV Format

CSV の書式の標準と、表計算ソフトが文字コードを取り違えないための印。時刻は日本時間で書き出すので、開いてそのまま読めます。

04

検知と定期実行Detection & Batch

Cloud
Run

Cloud RunServices

API と画面のアプリケーション。要求に応じて自動で増減します。記録を書くのはここで、業務の処理と同じ流れの中で書きます。

Cloud
Jobs

Cloud Run JobsBatch

定期処理の実行環境。1 つの実行ファイルに処理を束ね、起動時の引数でどれを走らせるか振り分けます。5 分ごとの検知も、月次の退避も、大きい CSV の書き出しも同じ器です。

Scheduler

Cloud SchedulerCron

時刻で処理を起動する仕組み。検知は 5 分ごと、退避は月次です。週次の子テーブルの整備と重ならないよう、起動の時刻をずらしています。

GoDaily Reconciliation

日次で Drive とアプリの状態を突き合わせる処理と、週次の存否確認。検知したアラートは Node.js 側と同じ 1 つのテーブルへ書きます。実行体が違っても、受け取り口は 1 つです。

Cloud
Monitoring

Cloud MonitoringMetrics

クラウド側の数値の観測。6 つの検知器のうち 1 つは、配信できなかったメッセージの数をここから読みます。読めなかったときは 0 件と報告せず、失敗として扱います。

Cloud
Logging

Cloud LoggingLogs

ログの集約と、ログの内容に一致したときの通知。アプリのログは重大度を独自の欄に入れているため、標準の重大度で絞る条件を書くと永久に一致しません。条件は本文の側で書きます。

Pub/SubMessaging

処理のきっかけを運ぶ仕組み。大きい CSV の完成を通知の仕組みへ伝えるのに使います。配信できなかったものは専用の置き場(デッドレター)へ退避し、その数が検知の対象になります。

Resend / React EmailDelivery

メールの配送と本文の組み立て。アラートは 29 種のうち 23 種がメールで届きます(共通の文面が 2 通、種別ごとの文面が 4 通)。

05

テストTesting

VitestUnit & Integration

純粋な関数(表示文の組み立て・続きの印の境界・同期と非同期の分岐・状態遷移の導出)と、実際のデータベースを使う結合テスト。範囲制限の規則も専用のテストで固定しています。

Playwright

PlaywrightEnd-to-End

画面を実際に操作する通しテスト。アクティビティの表示・アクセス履歴が Owner だけに見えること・絞り込み・続きの読み込みで行が重複しないこと、そしてアラート画面が権限の無い人から見えないことを確認します。

EXPLAIN

実行計画の固定Query Plan Assertion

10 万行を投入したうえで、使われる索引の集合をテストで宣言します。全件を走査する形に変わったらテストが落ちるので、行が増えてから気づく事態を避けられます。

この仕組みで特に注意して固定しているのは、「返っていないこと」を合格にしないという点です。たとえば「どんな続きの印を渡しても、閲覧の記録が 1 行増える」という規則は、印が未指定・空文字・復号できない・有効・未来の日時を詰めた細工、の 5 通りを表にして固定しています。記録が増えたことだけでなく、返ってきた中身も同時に確かめるので、「何も返さないから安全」という形の実装が通りません。

06

共通にした部分と分けた部分Shared or Separate

記録の仕組み(監査ログ・アクティビティ)と検知の仕組み(アラート)は、管理画面という同じ場所に並びます。土台の多くを共有できますが、片方の都合がもう片方へ漏れる場所は意図的に分けています。現在どちらになっているかの一覧です。

Shared or Separate
対象扱いそうしている理由
管理画面の共通部品
(計器カード・絞り込みチップ・ページ送り・理由つき確認ダイアログ・空状態)
共通同じ管理画面の兄弟ページ。部品を割ると、読み上げ対応やコントラストの是正を片方だけ直すことになる
日本語の表示名の置き場共通操作の種別 163 種も、アラートの種別 29 種も、共有の定数 1 か所に置く。画面の近くに置くと、内部の名前が画面へ漏れていないかの機械的な検査から外れる
権限の確認(ログイン → システム管理者 → データベース側の範囲制限)共通確認の順序と、権限が無いときに 404 を返す作法をそろえる。片方だけ 403 を返すと、そちらから存在が漏れる
定期処理の実行基盤共通5 分ごとの検知も、月次の退避も、大きい CSV の書き出しも同じ器から起動する。器を分けると監視の設定も分かれる
監査ログそのもの共通アラートの「対応履歴」は監査ログから引く。専用の履歴テーブルを作ると、同じ事実が 2 か所に載る
CSV の取り出しの作法
(理由の必須・日本時間・表計算で開ける形)
共通全社の監査ログ(19 列)と Box のアクティビティ(10 列)で列も読者も違うが、取り出し方の作法は同じにする
テーブル分ける記録は追記だけで更新も削除もできない、アラートは状態が動く。1 つに同居させると、更新の口を開けざるを得ず、追記専用が守れなくなる
月ごとの分割と退避記録の側だけ行の増え方の桁が違う。記録は業務の操作ごとに増え続けるが、アラートは異常があったときだけ増える
続きの取り方分ける記録は時系列に増え続けるので「この行の次から」で辿る。アラートは総数が小さく、行き来のしやすさを優先してページ番号にする
自動更新検知の側だけアラートは監視の画面なので既定で 30 秒ごとに更新する。記録は読む画面で、読んでいる最中に行が動くほうが邪魔になる
メールでの通知検知の側だけ記録は「後から辿るためのもの」で、書かれるたびに知らせる相手がいない。アラートは気づかれないと意味がない
Box のメンバーへの見せ方記録の側だけ同じ記録を、管理者には生のまま、Box のメンバーには出してよい項目だけに絞って見せる。アラートはシステム管理者だけが見るため、この作り分けを持たない
共通にするかどうかの判断の軸: 「同じ処理だから共通にする」ではなく、片方の都合で変えたときに、もう片方が壊れるかで決めています。部品・作法・語彙は共通にしても互いを縛りません。一方、テーブルや保持の規則は、共有した瞬間に片方の要求がもう片方の制約になります。追記だけを許す記録と、状態が動くアラートを 1 つのテーブルに入れれば、追記専用という一番守りたい性質のほうが折れます。