はじめに
Day 12の投稿後、PC破損により作業が中断していました。なんとか修理が完了したため連載を再開します。
今回はgoals機能の全面再設計について書きます。なぜ設計を捨てる判断をしたのか、その経緯と新設計の方針を記録します。
設計変更の発端
goals画面のレイアウト実装中に、right_areaに意図しない余白が発生する問題に気づきました。この問題を調査する過程で、そもそもgoals機能の画面構成を見直す必要があるという判断に至りました。
画面構成を見直す中で、データ構造自体の問題が浮かび上がりました。
フラット構造の問題
Day 4で設計した旧goalsのデータ構造は以下の通りです。
{
"key": "uuid",
"created_at": "YYYY-MM-DD",
"limit": "YYYY-MM-DD",
"task": "タスク名",
"status": -1,
"updated_at": null
}
このフラットな1レコード=1タスクの構造にはタスクの階層管理ができないという問題がありました。
「マイルストーン管理」を名乗るには、プロジェクト→作業ドメイン→個別タスクという階層でタスクを管理できる必要がありますが、以前のフラット構造では全てのタスクが同列に並ぶだけで、どのタスクがどのプロジェクトに属するかを表現できませんでした。
この点を改善点とみなし、改善前の設計はTODO管理ツールの域を出ていないと判断しました。
チケット形式への変更判断
上記の問題を解決するため、親チケット→子チケット→孫チケットの3階層構造に全面改修することを決めました。
新設計のデータ構造は以下の通りです。
なお、あくまでも実験段階における定義のため、後に修正される可能性があります。
{
"ticket_id": "uuid",
"title": "プロジェクト名",
"created_at": "YYYY-MM-DD",
"description": {
"overview": "プロジェクト概要",
"detail": "プロジェクト詳細情報"
},
"limit": "YYYY-MM-DD",
"work_domain": [
{
"domain_id": "uuid",
"label": [{"purpose": "作業目的", "work_domain": "作業ドメイン"}],
"overview": "作業概要",
"created_at": "HH:MM:SS",
"updated_at": null,
"states": 0,
"limit": "YYYY-MM-DD",
"completion_flag": false,
"task": [
{
"task_id": "uuid",
"title": "タスク名",
"created_at": "HH:MM:SS",
"limit": "YYYY-MM-DD",
"states": 0,
"updated_at": null
}
]
}
]
}
各階層の役割は以下の通りです。
| 階層 | 識別子 | 役割 |
|---|---|---|
| 親チケット | ticket_id | プロジェクト単位の管理。開始日・期限・概要・詳細を保持 |
| 子チケット(work_domain) | domain_id | 作業目的ごとの管理。改修・新規追加などの単位 |
| 孫チケット(task) | task_id | 個別タスクの管理。メソッド改修・モジュール追加などの細かな作業単位 |
改修のタイミングを今と判断した理由
改修のタイミングとしてgoals.tsが仮実装・バックエンド連携が未実装の現時点を選んだ理由は、フロント連携前で影響範囲が最小で済むためです。フロントエンドとの連携が完成した後に設計を変更すると、goals.py・goals.ts・main.py・backend_api.ts・JSONL構造の全てを作り直すことになります。現時点であれば連携が存在しないため、バックエンドの改修のみを行いswaggerを用いてAPIを叩くことで、ロジックの検証が行えると判断しました。
書き込みロジックの検証
新設計の書き込みロジックはtest.pyで動作検証を行っています。
def test_child_ticket():
id = uuid.uuid4()
path = create_dir('json/2026_05_goals.jsonl')
times = datetime.now().strftime('%H:%M:%S')
with open(path, 'r', encoding='utf-8') as f:
datas = [json.loads(line) for line in f.readlines()]
datas[0]['work_domain'].append({
"domain_id": str(id),
"label": [{'purpose': "作業目的", 'work_domain': "作業ドメイン"}],
"overview": "作業概要",
"created_at": times,
"updated_at": times,
"states": 0,
"limit": '2026-05-11',
"completion_flag": True,
"task":[]
})
with open(path, 'w', encoding='utf-8') as f:
for i in datas:
f.writelines(json.dumps(i, ensure_ascii=False) + '\n')
return datas[0]
親・子・孫の各チケット作成メソッドを分離して定義し、それぞれ独立したAPIから呼び出す設計にする方針です。
画面構成の変更
データ構造の変更に伴い、goals画面の構成も変更します。
| 変更内容 | 理由 |
|---|---|
| カレンダー表示エリアを追加 | 期限管理を視覚的に確認できるようにするため |
| 統計エリアを廃止 | チケット形式では統計よりカレンダーが適していると判断 |
現在の状態と今後
goals機能の改修は現在進行中です。データ定義の調整が完了次第、goals.py・goals.ts・main.py・backend_api.tsの改修に着手します。各チケット階層ごとにAPIを分離する設計は確定していますが、フロント側からUUIDを指定して操作する具体的な実装方法は現時点では未解決であり、実装と並行して調査を進める予定です。
改修が完了した段階で別途記事にまとめます。
おわりに
今日はgoals機能の全面再設計の経緯について書きました。
設計を捨てる判断は後退に見えるかもしれませんが、「マイルストーン管理ツール」として動作するための必要な変更です。仮実装・未連携の段階で問題に気づけたことは、むしろ早期発見だったと考えています。
Day 14ではPC破損による中断の経緯と、今後の連載方針について書きます。
この記事は連載「クラウドに依存しないマイルストーン管理ツール開発記」のDay 13です。
- Day 1 - なぜ自作するのか
- Day 2 - 技術スタックとその選定理由
- Day 3 - Pythonで時間記録機能を作る
- Day 4 - タスク管理のデータ構造
- Day 5 - FastAPIによるAPIハブ設計
- Day 6 - TypeScriptでクラスベースのコンポーネント設計
- Day 7 - 1週間の振り返りと今後の開発方針
- Day 8 - データ保存先の設計変更
- Day 9 - CSVフォーマットの設計
- Day 10 - ファイル読み込みモジュールの設計
- Day 11 - APIクライアント自動生成の導入
- Day 12 - timer画面の本実装と非同期通信
- Day 14 - PC破損・中断経緯と今後の連載方針