Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
令和最新技術で伝統掲示板を再構築: HonoX で作る型安全なスレッドフロート型掲示板 / か...
Search
calloc134
October 17, 2025
Research
820
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
令和最新技術で伝統掲示板を再構築: HonoX で作る型安全なスレッドフロート型掲示板 / かろっく@calloc134 - Hono Conference 2025
Hono Conference 2025 で発表するスライドです。
calloc134
October 17, 2025
More Decks by calloc134
See All by calloc134
認証認可を通して学ぶ、『わからない』のつぶし方 - ID沼入口 発表資料
calloc134
11
4.9k
Understanding the Internal Structure of React (React Tokyo #6 - @calloc134)
calloc134
1
180
Reactの内部構造を知っておく (React Tokyo #6 - @calloc134)
calloc134
51
16k
端折りまくって学ぶReactの内部 レンダー・コミット編
calloc134
9
2.4k
バックエンドにおけるSQLへのアプローチ比較(+SafeQLの紹介) matsuriba tech vol.7
calloc134
0
360
IDEA Web班定例会 2024/2/12
calloc134
0
150
かろっく自己紹介
calloc134
3
5.9k
【クリスマスLTパーティー】大学の授業でCloudflareフル活用なシステム開発をした話
calloc134
0
170
IDEA Web班定例会 LTゆるゆる
calloc134
0
110
Other Decks in Research
See All in Research
Physical AIでモデリングはどう変わるか/How Physical AI Will Change Modeling
stktu
0
280
実例から見るLLMのマンガ理解:実務VQAタスクによる長期的文脈と視覚情報の定性評価
kzmssk
0
170
LA-Bench 2025:実験指示から実行可能手順を生成するためのデータセット/LA-Bench 2025: A Dataset for Generating Executable Experimental Procedures from Experimental Instructions
stktu
0
190
Research Engineerという仕事 / Research Engineering: Bridging Research and Business
chck
1
360
Vector Map as Language: Toward Unified Remote Sensing Vector Mapping
satai
3
300
[CV勉強会@関東 CVPR2026] PSDesigner: Automated Graphic Design with a Human-Like Creative Workflow / kantocv 67th CVPR 2026
shunk031
0
360
長時間動画QAにおけるマルチエージェント推論 ・SVAgent: Storyline-Guided Long Video Understanding via Cross-Modal Multi-Agent Collaboration
murakawatakuya
1
220
適応的スパムフィルタのための軽量な類似メッセージカウンタ / jsai2026-adaptive-spam-filter
monochromegane
0
5.7k
ふとした出会いで生まれたSkillが、 社内利用1位になるまで
mikimhk
22
24k
HAKARI-Bench - 実運用視点での情報検索モデル評価ベンチマーク
hotchpotch
1
780
[SNLP2026] Scaling up Test-Time Compute with Latent Reasoning: A Recurrent Depth Approach
wataruuuuu
0
330
Source Code Diff Revolution
tsantalis
0
180
Featured
See All Featured
Building a Scalable Design System with Sketch
lauravandoore
464
34k
Chasing Engaging Ingredients in Design
codingconduct
0
340
Believing is Seeing
oripsolob
1
240
Are puppies a ranking factor?
jonoalderson
2
4k
Ethics towards AI in product and experience design
skipperchong
2
410
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
350
Faster Mobile Websites
deanohume
310
32k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Utilizing Notion as your number one productivity tool
mfonobong
4
610
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
HDC tutorial
michielstock
2
930
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>
HonoXの型安全なUI構築例 11
コア技術(2): SafeQL ESLintプラグインとして提供され、型情報を生成するSQLクエリチェッカー ORMの安全性 と 生SQLの表現力 を両立 主な機能 開発中にクエリのミスや型の不一致を リアルタイムで検出
クエリ結果の型を 正確に推論・提案 本番環境への影響をゼロに抑えることができるという利点も ※ 詳しくは技術記事を書いたので、そちらもご覧ください。 https://zenn.dev/calloc134/articles/4a8c1af0eb5aae 12
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 `; 13
SafeQLの型情報生成例 14
その他を支える技術スタック Tailwind CSS: ユーティリティファーストなCSSで効率的なスタイリング Bun: 高速なJSランタイム。特に優れた開発体験(DX)を提供 Docker: 開発・本番環境の構築を簡素化 Cloudflare Workers:
エッジコンピューティング環境 へのデプロイ 15
比較: 伝統的BBS vs VakKarma 項目 伝統的なBBS (例: Perl/PHP) VakKarma UIレンダリング
文字列ベースのテンプレート 型安全なJSX (HonoX) DBクエリ 手動でのSQL文字列組み立て レスポンスが型安全となったSQL (SafeQL) アーキテクチャ 基本はハンドラにベタ書き 責務やレイヤの分割されたDDD エラー検出 多くが実行時エラー コンパイル時 / リント時エラー 16
2. The "How" どうやって作ったのか? 17
設計思想の柱: 型安全性の徹底追求 哲学 DBスキーマからブラウザのHTML要素まで、データの型を常に保証 目的 型の力で未然に防げるエラーをできるだけ開発時に削減 実現方法 HonoXとSafeQLの組み合わせによる 「型安全の連続的な連鎖」 18
設計思想の柱: ドメイン駆動設計(DDD) ドメイン駆動設計(DDD) とは? ソフトウェアで解決したい領域(ドメイン) のモデリングに焦点を当てる設計アプローチ VakKarmaにおけるドメイン 「スレッド」 「レス」 「トリップ」
「ID」といった掲示板を定義する概念やルール 目的 ドメインの概念をコードに正しく落とし込み、ルールを散逸させない べた書きのコードと比べ、 何を達成しているかが明確なコード を実現 集約外の情報を取得したいときは高階関数パターンを利用 細かい手法にはこだわらず柔軟に、DDDの恩恵を十分に享受できるよう 戦略を優先させる 19
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])?)*$/; 20
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; }; 22
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アクセスなど技術的関心事 25
DDD サンプルコード: ディレクトリ構造 (1) ├── conversation // 掲示板のコアに関する関心事 │ ├──
domain │ ├── repositories │ └── usecases ├── config // 設定ファイルの関心事 │ ├── domain │ ├── repositories │ └── usecases └── shared // 共通のコード ├── types // 型定義やエラー定義 └── utils // 汎用的なユーティリティ関数 26
DDD サンプルコード: ディレクトリ構造 (2) ├── conversation │ ├── domain │
│ ├── read // 読み込み系ドメイン 内部からの信頼できるオブジェクト │ │ │ ├── ReadAuthorName.ts │ │ │ └── ... │ │ └── write // 書き込み系ドメイン 外部からの信頼できないオブジェクト │ │ ├── WriteAuthorName.test.ts │ │ ├── WriteAuthorName.ts │ │ └── ... │ ├── repositories // リポジトリ層 │ │ ├── createResponseByThreadIdRepository.ts │ │ ├── getAllThreadsRepository.ts │ │ └── ... │ └── usecases // ユースケース層 │ ├── getTopPageUsecase.ts │ ├── postResponseByThreadIdUsecase.ts │ └── ... └── ... 27
DDDの実践: コード品質を高める工夫 neverthrow によるエラーハンドリング 例外ではなく Result 型を返す設計の採用 エラーと例外を分離する考え方 Tagged Union
パターン を利用したドメインオブジェクト定義 string 型ではなく独自の UserName 型などを定義 異なる型の値の意図しない混同を、型レベル設計で防止 DBロジックの完全な隔離 SQLクエリはリポジトリ層に完全に封じ込め、システムのモジュール性を向上 28
3. The "What" 何ができたのか? 29
デモサイト URL: https://vakkarma-main.calloc134personal.workers.dev/ Cloudflare Workers上で動作 データストアはSupabase Databaseの無料枠を利用 (日本リージョンを利用) 30
トップページ 上位30件のスレッドを表示 上位10件は最初のレスと最新のレスをプレビュー レス投稿フォーム、新規スレッド作成フォームを配置 31
トップページ (1) 32
トップページ (2) 33
スレッド詳細ページ スレッドの全てのレスを表示 おなじみのフォーマット レス番号、名前、投稿日時、ID、本文 レス投稿フォームを配置 34
スレッド詳細ページ (1) 35
スレッド詳細ページ (2) 36
管理画面 /admin にアクセスし、パスワード認証でログイン 設定可能な項目 掲示板名、ローカルルール 名無しのデフォルト名 スレッド・レスの最大文字数 管理者パスワードの変更 37
管理画面 38
投稿の仕様: トリップとID トリップ機能 ユーザ名に # を含めることで、ユニークな識別子を生成 オリジナルより安全性を高めた独自の生成ロジックを採用 DBにはハッシュ化されたトリップのみ保存 ID IPアドレスと日時から一意に生成
39
レスポンシブ対応 Tailwind CSS を使用 スマートフォンからデスクトップまで 様々な画面サイズで表示を最適化 40
思想としての「No-JS」 クライアントサイドJavaScript不要 全ての機能がJavaScriptを無効化した環境でも完全に動作 JavaScriptがあれば、より良いUXを提供 利点 アクセシビリティ: Tor Browser のような、プライバシー重視の環境 でも利用可能
パフォーマンス: 初期ページの表示が極めて高速 41
ChMate(専用ブラウザ)との互換性 ChMate のような 専用ブラウザからのアクセスを 公式にサポート /senbura/ エンドポイントを実装 ChMateが要求する特殊フォーマットに対応 閲覧だけでなく、レス投稿やスレッド作成も可能 42
4. The "Next Steps" 何を学んだのか? 43
今後の展望 アプリケーション機能 NGワード、レス検索、Captcha導入 スレッド・レスの削除・ロック機能 副次的な機能 テスト、ロギングの強化 セルフホスト向けサポートの充実 (https化, Hidden Service化)
Docker Composeでの簡単セットアップ 44
学びの共有 HonoX はMPAを作成する上で非常に優れたモダンフレームワーク SafeQL は型安全性と生SQLの表現力を両立できる強力なツール ドメイン駆動設計(DDD) で表現の漏れを防ぎ、保守性を向上 45
まとめ 達成したこと クラシックなWebアプリを最新技術で現代化 DBからUIまで一貫したエンドツーエンドの型安全性を実現 ドメイン駆動設計(DDD) により保守性と拡張性の高いコードベースを構築 オリジナルの尊重と互換性維持・現代的なUXや開発体験を両立 46
ご清聴ありがとうございました ぜひ触ってみてください! デモ: https://vakkarma-main.calloc134personal.workers.dev GitHub: https://github.com/calloc134/vakkarma-main (Starをいただけると開発の励みになります!) 47