コンテンツにスキップ

教材管理システムを構築します。 ユースケースとして、管理者、教師、メンター、受講者がいます。 教師は、コンテンツを提供する人です。作成者には教師の名前が入ります。 教師は、自身のアカウントでコンテンツを作成し、できたら保存か申請ができます。申請を出すと管理者承認し、公開となります。 公開されたコンテンツは受講者が受講できます。 メンターは、受講者の質問対応や学習計画のフォローをする人です。今回のシステム内には機能としては登場しないかもしれませんが、将来的に追加する可能性があります。

教材構成

コース > セクション > サブセクション > サブサブセクション > ... > レッスン

コースにセクションがあります。 セクションにレッスンを紐づけます。 セクションは親子構造にして、階層を設計上は無限にできるようにします。

レッスンにはコンテンツを1つ以上紐づけます。 コンテンツには、名称、概要、コンテンツタイプ、作成者、ステータス(下書き、審査中、公開待ち、公開中)、公開日時などは共通で管理します。コンテンツタイプは、ビデオ、テキスト、クイズ(選択式問題)、ファイルなどの分類をします。 ビデオコンテンツは、youtubeやVimeoのリンクを指定する方法と、Cloudflare R2で独自に構築する方法で、ブラウザ内でのストリーミング配信にしたいです。名称、概要、ビデオ種別、ビデオID、ビデオURLは含めてください。

テキストコンテンツは、名称、概要、本文を含めてください。本文をプレーンテキストか、マークダウン形式で入力し、表示の際にはデザインされたHTML形式で表示されるようにしたいです。

クイズコンテンツは、クイズカテゴリ、名称、問題文と選択肢があり、複数の選択肢から正解を選び正解を判定するようにしたいです。基本的には4択式で1つが正解を考えいますが、6択から正解を2つを選ぶような問題も作れるように汎用的な構造にしたいです。

ファイルコンテンツは、名称、概要、ファイル種別を含めてください。PDF、zip、txt、オフィス形式のファイルなどをタイトルと説明付きで登録し、ダウンロードが可能なようにしたいです。

役割と使用技術

Java、Spring、JPA、MyBatis、MySQL、SpringSecurity、Thymeleafを効率的に利用したいです。 以下をベースに設計してください。

クラス 役割 使用技術
Entity テーブル1件を表す。登録・更新・削除を担当 JPA
Repository Entityに対するCRUDを担当 Spring Data JPA
Mapper JOIN・集計・検索などのSQLを担当 MyBatis
DTO 画面表示やAPIレスポンス用のデータ 共通
Form 入力値の受け取りとバリデーション Spring MVC
SearchCondition 検索条件の受け渡し MyBatis
Service ビジネスロジックを実装し、JPAとMyBatisを組み合わせる Spring

まずは、1段階をお願いします。


とても良い設計方針だと思います。

この規模になると、最初からテーブルを作り始めるよりも、DDD(ドメイン駆動設計)まではいかなくても、「ドメイン設計 → DB設計 → Entity → Service」 の順番で設計すると後々の保守性が大きく変わります。

私であれば、このシステムは以下のように 5段階 に分けて設計します。

段階 内容
第1段階 ドメイン設計(機能・責務・画面・ERの土台)
第2段階 DB設計(DDL・ER図・インデックス)
第3段階 Springプロジェクト構成(Entity・Repository・Mapper・DTO・Formなど)
第4段階 教材作成・承認・公開フロー
第5段階 受講機能・学習履歴・進捗・クイズ・メンター機能

第1段階 ドメイン設計

まずは「何を管理するシステムか」を整理します。


システム全体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ユーザー
│
├─ 管理者
│      ├ 教師管理
│      ├ 教材承認
│      ├ ユーザー管理
│      └ 公開管理
│
├─ 教師
│      ├ コース作成
│      ├ レッスン作成
│      ├ コンテンツ作成
│      └ 公開申請
│
├─ 受講者
│      ├ コース受講
│      ├ クイズ回答
│      ├ ダウンロード
│      └ 学習履歴
│
└─ メンター(将来)
       ├ 受講状況確認
       ├ 学習相談
       └ コメント

ドメイン一覧

まずは管理する情報を決めます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
User
Role
Course
Section
Lesson
Content
VideoContent
TextContent
QuizContent
FileContent

Category

Quiz
Question
Choice

LearningHistory

Approval

Notification

このくらいに分けるとかなり整理しやすくなります。


教材構造

ご要望どおり

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Course
   │
   ├ Section
   │
   ├ Section
   │      │
   │      ├ Section
   │      │
   │      ├ Section
   │      │
   │      └ Lesson
   │
   └ Lesson

Sectionを親子構造にします。

1
2
3
4
5
6
7
Section

id

course_id

parent_section_id

これだけで

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
第1章

 1-1

   1-1-1

      Lesson

 1-2

    Lesson

のような無限階層になります。


Lesson

Lessonは教材のまとまりです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Javaとは

│

├ 動画

├ テキスト

├ PDF

└ クイズ

つまり

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Lesson

↓

Content

↓

Video
Text
Quiz
File

という構造になります。


Content

ここが重要です。

共通情報だけを持たせます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Content

id

lesson_id

content_type

title

summary

author_id

status

published_at

display_order

created_at

updated_at

動画でも

テキストでも

PDFでも

全部ここを通ります。


コンテンツ種別

1
2
3
4
5
6
7
VIDEO

TEXT

QUIZ

FILE

Enum管理がおすすめです。


Video

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
VideoContent

content_id

video_type

video_id

video_url

duration

thumbnail

video_type

1
2
3
4
5
YOUTUBE

VIMEO

R2

R2の場合

1
2
3
4
5
video_url

signed_url

stream_key

などを追加してもいいです。


Text

1
2
3
4
5
6
7
TextContent

content_id

body

markdown_flag

表示時

1
2
3
4
5
6
7
8
9
Markdown

↓

HTML

↓

Thymeleaf

になります。


Quiz

Quizは少し汎用的に作ります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Quiz

id

content_id

category

question

multiple_answer

answer_count

例えば

1
2
3
4択

正解1つ

なら

1
2
3
multiple_answer=false

answer_count=1

6択で

正解2つ

なら

1
2
3
multiple_answer=true

answer_count=2

になります。


Choice

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Choice

id

quiz_id

choice_text

is_correct

display_order

何択でも対応できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
A

B

C

D

E

F

全部Choiceになります。


File

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
FileContent

content_id

file_type

file_name

storage_path

file_size

download_count

file_type

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
PDF

ZIP

TXT

EXCEL

WORD

POWERPOINT

OTHER

承認フロー

教師

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
作成

↓

保存(下書き)

↓

申請

↓

管理者承認

↓

公開待ち

↓

公開

状態は

1
2
3
4
5
6
7
8
9
DRAFT

REVIEWING

WAITING_PUBLISH

PUBLISHED

REJECTED

くらいがおすすめです。


学習履歴

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
受講者

↓

Lesson

↓

Content

↓

閲覧

↓

クイズ

↓

完了

別テーブル

1
LearningHistory

で管理します。


Spring構成

ご提示いただいた構成は非常に良く、このシステムにも適しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
controller

service

repository

mapper

entity

dto

form

search

config

security

exception

validator

common

JPA と MyBatis の役割分担

今回の規模では、以下のように役割を明確に分けると保守しやすくなります。

クラス 役割 技術
Entity 1テーブル1レコードを表現し、永続化対象とする JPA
Repository 単一テーブルのCRUD(登録・更新・削除・主キー検索) Spring Data JPA
Mapper JOIN・集計・検索・一覧表示・ツリー取得などのSQL MyBatis
DTO 画面表示・API・一覧・詳細など用途別データ 共通
Form 画面入力とバリデーション Spring MVC
SearchCondition 検索条件の受け渡し(キーワード・ステータス・作成者など) MyBatis
Service トランザクション制御と業務ロジック。RepositoryとMapperを組み合わせる Spring

この役割分担にすると、更新処理はJPAに集約され、一方で教材ツリーや一覧検索のような複雑なSQLはMyBatisに任せられるため、それぞれの長所を活かせます。


第2段階で設計する内容

次の段階では、このドメイン設計を具体的なデータモデルへ落とし込みます。

  • 全テーブル一覧(約20~30テーブルを想定)

  • ER図

  • MySQL DDL

  • JPA Entity設計

  • 外部キー・インデックス設計

  • Enum設計(ロール、ステータス、コンテンツ種別など)

  • 監査項目(作成者・更新者・作成日時・更新日時)の共通化

  • 承認フローや学習履歴を考慮した正規化

この順番で進めることで、その後のSpring Boot実装まで一貫性のある設計にできます。