Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
令和最新技術で伝統掲示板を再構築: HonoX で作る型安全なスレッドフロート型掲示板 / か...
Search
calloc134
October 17, 2025
Research
0
110
令和最新技術で伝統掲示板を再構築: HonoX で作る型安全なスレッドフロート型掲示板 / かろっく@calloc134 - Hono Conference 2025
Hono Conference 2025 で発表するスライドです。
calloc134
October 17, 2025
Tweet
Share
More Decks by calloc134
See All by calloc134
Understanding the Internal Structure of React (React Tokyo #6 - @calloc134)
calloc134
0
55
Reactの内部構造を知っておく (React Tokyo #6 - @calloc134)
calloc134
50
15k
端折りまくって学ぶReactの内部 レンダー・コミット編
calloc134
9
2.2k
バックエンドにおけるSQLへのアプローチ比較(+SafeQLの紹介) matsuriba tech vol.7
calloc134
0
210
IDEA Web班定例会 2024/2/12
calloc134
0
100
かろっく自己紹介
calloc134
2
4k
【クリスマスLTパーティー】大学の授業でCloudflareフル活用なシステム開発をした話
calloc134
0
110
IDEA Web班定例会 LTゆるゆる
calloc134
0
61
Matsuriba_Tech第二回LT.pdf
calloc134
0
61
Other Decks in Research
See All in Research
単施設でできる臨床研究の考え方
shuntaros
0
3k
MIRU2025 チュートリアル講演「ロボット基盤モデルの最前線」
haraduka
15
8.7k
財務諸表監査のための逐次検定
masakat0
0
140
When Submarine Cables Go Dark: Examining the Web Services Resilience Amid Global Internet Disruptions
irvin
0
320
引力・斥力を制御可能なランダム部分集合の確率分布
wasyro
0
260
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
660
PhD Defense 2025: Visual Understanding of Human Hands in Interactions
tkhkaeio
1
250
AlphaEarth Foundations: An embedding field model for accurate and efficient global mapping from sparse label data
satai
3
350
Panopticon: Advancing Any-Sensor Foundation Models for Earth Observation
satai
3
230
Time to Cash: The Full Stack Breakdown of Modern ATM Attacks
ratatata
0
160
20250624_熊本経済同友会6月例会講演
trafficbrain
1
670
CoRL2025速報
rpc
1
2k
Featured
See All Featured
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
20
1.2k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
252
21k
Balancing Empowerment & Direction
lara
4
690
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Java REST API Framework Comparison - PWX 2021
mraible
34
8.9k
Site-Speed That Sticks
csswizardry
12
900
Statistics for Hackers
jakevdp
799
220k
Agile that works and the tools we love
rasmusluckow
331
21k
Speed Design
sergeychernyshev
32
1.2k
Music & Morning Musume
bryan
46
6.8k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
667
120k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
37
2.6k
Transcript
VakKarma - HonoXで作る型安全で現代的なスレッドフロート型掲示板 かろっく (@calloc134) 1
自己紹介 かろっく @calloc134 ソフトウェアエンジニア志望 (26卒) TypeScript や Rust を主に利用 アプリケーション領域
(フロントエンド・バックエンド) を主に担当 データ基盤・インフラ・セキュリティなど幅広く・・・ 最近は新しい技術の探究、コードリーディング(React)・RFC読解(OAuth/OIDC)に注力 GitHub | Twitter | Zenn 2
はじめに スレッドフロート型掲示板 とは 日本のインターネット文化を形成した2ちゃんねるの形式 "VakKarma" とは? 伝統的な掲示板を 現代のWeb開発技術で再実装 したプロジェクト プロジェクトの核心
徹底的な 型安全性 の追求 ドメイン駆動設計(DDD) の適用 3
本日のアジェンダ 1. The "Why": なぜVakKarmaを作ったのか? (動機と技術選定) 2. The "How": どうやって作ったのか?
(型安全性とDDD) 3. The "What": 何ができたのか? (機能とデモ) 4. The "Next Steps": 今後の展望と学び 4
1. The "Why" どのようなモチベで作ったのか? 5
スレッドフロート型掲示板とは スレッドフロート型掲示板 新しい投稿でスレッドが 「浮上(フロート)」 する仕組み 1998年「multi2」で実装され、 「あめぞう」を経て「2ちゃんねる」へ発展 匿名文化の中心地 自由な議論の場 数々のインターネット・ミームや文化の発信源
6
プロジェクト「VakKarma」 プロジェクト「VakKarma」 コンセプト 2ちゃんねる風掲示板の現代的再実装 「ゼロちゃんねるプラス」 のUIを参考に開発 目的 現代のレベルに合わせた 堅牢性、 保守性
、そして 優れた開発体験 を実現する
技術選定の目的と方針 プロジェクトの核心 型安全性 と 優れた開発体験 を UIからデータベースに至る全てのレイヤー で実現 エコシステム 単なる流行のツールの寄せ集めではない
明確な目的・方針 を意図した技術スタック 8
コア技術(1): HonoX Hono をベースとしてviteを組み合わせたメタフレームワーク MPAの効率的な構築を支援 JSXによる型安全なUI構築 従来のテンプレートエンジン: 型情報がなく、実行時エラーの温床に HonoX: コントローラから直接JSXを返却
Reactのような開発体験でHTML要素を記述でき、型安全性を実現 Honoのエコシステム 軽量・高速なHonoのミドルウェアやプラグインをそのまま活用可能 9
HonoX サンプルコード return c.render( // ... <ul className="flex flex-wrap gap-4">
{threadTop30.map((thread, index) => ( <li key={thread.id.val} className="flex-none max-w-md"> <a className="text-purple-600 underline whitespace-normal break-words" href={ index < 10 ? `#thread-${thread.id.val}` : `/threads/${thread.id.val}/l50` } > {index + 1}: {thread.title.val} ({thread.countResponse}) </a> </li> ))} </ul>
コア技術(2): SafeQL ESLintプラグインとして提供され、型情報を生成するSQLクエリチェッカー ORMの安全性 と 生SQLの表現力 を両立 主な機能 開発中にクエリのミスや型の不一致を リアルタイムで検出
クエリ結果の型を 正確に推論・提案 本番環境への影響をゼロに抑えることができるという利点も 11
SafeQL サンプルコード const result = await sql<{ id: string }[]>`
INSERT INTO threads( id, title, posted_at, updated_at, epoch_id ) VALUES( ${thread.id.val}::uuid, ${thread.title.val}, ${thread.postedAt.val}, ${thread.updatedAt.val}, ${thread.epochId.val} ) RETURNING id `; 12
その他を支える技術スタック Tailwind CSS: ユーティリティファーストなCSSで効率的なスタイリング Bun: 高速なJSランタイム。特に優れた開発体験(DX)を提供 Docker: 開発・本番環境の構築を簡素化 Cloudflare Workers:
エッジコンピューティング環境 へのデプロイ 13
比較: 伝統的BBS vs VakKarma 項目 伝統的なBBS (例: Perl/PHP) VakKarma UIレンダリング
文字列ベースのテンプレート 型安全なJSX (HonoX) DBクエリ 手動でのSQL文字列組み立て レスポンスが型安全となったSQL (SafeQL) アーキテクチャ 基本はハンドラにベタ書き 責務やレイヤの分割されたDDD エラー検出 多くが実行時エラー コンパイル時 / リント時エラー 14
2. The "How" どうやって作ったのか? 15
設計思想の柱: 型安全性の徹底追求 哲学 DBスキーマからブラウザのHTML要素まで、データの型を常に保証 目的 型の力で未然に防げるエラーをできるだけ開発時に削減 実現方法 HonoXとSafeQLの組み合わせによる 「型安全の連続的な連鎖」 16
設計思想の柱: ドメイン駆動設計(DDD) ドメイン駆動設計(DDD) とは? ソフトウェアで解決したい領域(ドメイン) のモデリングに焦点を当てる設計アプローチ VakKarmaにおけるドメイン 「スレッド」 「レス」 「トリップ」
「ID」といった掲示板を定義する概念やルール 目的 ドメインの概念をコードに正しく落とし込み、ルールを散逸させない べた書きのコードと比べ、 何を達成しているかが明確なコード を実現 集約外の情報を取得したいときは高階関数パターンを利用 細かい手法にはこだわらず柔軟に、DDDの恩恵を十分に享受できるよう 戦略を優先させる 17
DDD サンプルコード: メールアドレス(1) // メールアドレス export type WriteMail = {
readonly _type: "WriteMail"; readonly val: string; }; // https://zenn.dev/igz0/articles/email-validation-regex-best-practices const regexMail = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; 18
DDD サンプルコード: メールアドレス(2) export const createWriteMail = ( value: string
| null ): Result<WriteMail, ValidationError> => { if (value === null) { return ok({ _type: "WriteMail", val: "" }); } if (value.length > 255) { return err(new ValidationError("メールアドレスは255文字以内です")); } // 簡単なメールアドレス形式チェック (厳密なものではない) if ( value !== "" && value.toLowerCase() !== "sage" && !regexMail.test(value) ) { return err(new ValidationError("不正なメールアドレス形式です")); }
DDD サンプルコード: 投稿者名(1) type SomeWriteAuthorName = { readonly _type: "some";
readonly authorName: string; readonly trip: string; }; type NoneWriteAuthorName = { readonly _type: "none"; readonly authorName: string; }; // 投稿者名 export type WriteAuthorName = { readonly _type: "WriteAuthorName"; // readonly val: string; // some/noneパターン readonly val: SomeWriteAuthorName | NoneWriteAuthorName; }; 20
DDD サンプルコード: 投稿者名(2) export const createWriteAuthorName = async ( authorName:
string | null, // 高階関数パターンで、より低レイヤの処理を隠蔽できるようにする getDefaultAuthorName: () => Promise<Result<string, Error>> ): Promise<Result<WriteAuthorName, ValidationError>> => { if (!authorName) { const nanashiName = await getDefaultAuthorName(); if (nanashiName.isErr()) { return err(nanashiName.error); } return ok({ _type: "WriteAuthorName", val: { _type: "none", authorName: nanashiName.value, }, }); }
DDD サンプルコード: 投稿者名(3) // ... 続き if (authorName.includes("#")) { const
[name, tripKey] = authorName.split("#"); const trip = createTrip(tripKey); return ok({ _type: "WriteAuthorName", val: { _type: "some", authorName: name, trip: trip, }, }); } return ok({ _type: "WriteAuthorName", val: { _type: "none", authorName, },
DDDの実践: クリーンアーキテクチャに似たレイヤードアーキテクチャ 関心の分離 コードベースを明確な責務を持つ層に分割 多すぎるレイヤ分けを避け、シンプルさを維持 VakKarmaのディレクトリ構造 ハンドラ層 (UI層): HonoXのハンドラ/ルーティング ユースケース層:
アプリケーションの具体的な操作 ドメイン層: ビジネスロジックの心臓部 リポジトリ層 (インフラ層): DBアクセスなど技術的関心事 23
DDD サンプルコード: ディレクトリ構造 (1) ├── conversation // 掲示板のコアに関する関心事 │ ├──
domain │ ├── repositories │ └── usecases ├── config // 設定ファイルの関心事 │ ├── domain │ ├── repositories │ └── usecases └── shared // 共通のコード ├── types // 型定義やエラー定義 └── utils // 汎用的なユーティリティ関数 24
DDD サンプルコード: ディレクトリ構造 (2) ├── conversation │ ├── domain │
│ ├── read // 読み込み系ドメイン 内部からの信頼できるオブジェクト │ │ │ ├── ReadAuthorName.ts │ │ │ └── ... │ │ └── write // 書き込み系ドメイン 外部からの信頼できないオブジェクト │ │ ├── WriteAuthorName.test.ts │ │ ├── WriteAuthorName.ts │ │ └── ... │ ├── repositories // リポジトリ層 │ │ ├── createResponseByThreadIdRepository.ts │ │ ├── getAllThreadsRepository.ts │ │ └── ... │ └── usecases // ユースケース層 │ ├── getTopPageUsecase.ts │ ├── postResponseByThreadIdUsecase.ts │ └── ... └── ... 25
DDDの実践: コード品質を高める工夫 neverthrow によるエラーハンドリング 例外ではなく Result 型を返す設計の採用 エラーと例外を分離する考え方 Tagged Union
パターン を利用したドメインオブジェクト定義 string 型ではなく独自の UserName 型などを定義 異なる型の値の意図しない混同を、型レベル設計で防止 DBロジックの完全な隔離 SQLクエリはリポジトリ層に完全に封じ込め、システムのモジュール性を向上 26
3. The "What" 何ができたのか? 27
デモサイト URL: https://vakkarma-main.calloc134personal.workers.dev/ Cloudflare Workers上で動作 データストアはSupabase Databaseの無料枠を利用 (日本リージョンを利用) 28
トップページ 上位30件のスレッドを表示 上位10件は最初のレスと最新のレスをプレビュー レス投稿フォーム、新規スレッド作成フォームを配置 29
トップページ (1) 30
トップページ (2) 31
スレッド詳細ページ スレッドの全てのレスを表示 おなじみのフォーマット レス番号、名前、投稿日時、ID、本文 レス投稿フォームを配置 32
スレッド詳細ページ (1) 33
スレッド詳細ページ (2) 34
管理画面 /admin にアクセスし、パスワード認証でログイン 設定可能な項目 掲示板名、ローカルルール 名無しのデフォルト名 スレッド・レスの最大文字数 管理者パスワードの変更 35
管理画面 36
投稿の仕様: トリップとID トリップ機能 ユーザ名に # を含めることで、ユニークな識別子を生成 オリジナルより安全性を高めた独自の生成ロジックを採用 DBにはハッシュ化されたトリップのみ保存 ID IPアドレスと日時から一意に生成
37
レスポンシブ対応 Tailwind CSS を使用 スマートフォンからデスクトップまで 様々な画面サイズで表示を最適化 38
思想としての「No-JS」 クライアントサイドJavaScript不要 全ての機能がJavaScriptを無効化した環境でも完全に動作 JavaScriptがあれば、より良いUXを提供 利点 アクセシビリティ: Tor Browser のような、プライバシー重視の環境 でも利用可能
パフォーマンス: 初期ページの表示が極めて高速 39
ChMate(専用ブラウザ)との互換性 ChMate のような 専用ブラウザからのアクセスを 公式にサポート /senbura/ エンドポイントを実装 ChMateが要求する特殊フォーマットに対応 閲覧だけでなく、レス投稿やスレッド作成も可能 40
4. The "Next Steps" 何を学んだのか? 41
今後の展望 アプリケーション機能 NGワード、レス検索、Captcha導入 スレッド・レスの削除・ロック機能 副次的な機能 テスト、ロギングの強化 セルフホスト向けサポートの充実 (https化, Hidden Service化)
Docker Composeでの簡単セットアップ 42
学びの共有 HonoX はMPAを作成する上で非常に優れたモダンフレームワーク SafeQL は型安全性と生SQLの表現力を両立できる強力なツール ドメイン駆動設計(DDD) で表現の漏れを防ぎ、保守性を向上 43
まとめ 達成したこと クラシックなWebアプリを最新技術で現代化 DBからUIまで一貫したエンドツーエンドの型安全性を実現 ドメイン駆動設計(DDD) により保守性と拡張性の高いコードベースを構築 オリジナルの尊重と互換性維持・現代的なUXや開発体験を両立 44
ご清聴ありがとうございました ぜひ触ってみてください! デモ: https://vakkarma-main.calloc134personal.workers.dev GitHub: https://github.com/calloc134/vakkarma-main (Starをいただけると開発の励みになります!) 45