ありがとうございます。
ここからは 第4段階:Spring Boot実装アーキテクチャ設計 に進みます。
今回は採用した「教材ライブラリ方式」を前提に、以下を設計します。
-
パッケージ構成
-
Entity設計方針(JPA)
-
Repository設計
-
MyBatis Mapper設計
-
DTO設計
-
Form設計
-
Service責務
-
Controller構成
-
Spring Security連携
1. 全体パッケージ構成
Spring Boot 3 + JPA + MyBatisでは、レイヤー単位で分離します。
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 | com.example.lms
├── LmsApplication.java
│
├── config
│ ├── SecurityConfig.java
│ ├── JpaConfig.java
│ └── MyBatisConfig.java
│
├── security
│ ├── CustomUserDetails.java
│ ├── CustomUserDetailsService.java
│ └── LoginUser.java
│
├── controller
│ ├── admin
│ ├── teacher
│ ├── student
│ └── common
│
├── service
│ ├── ContentService.java
│ ├── CourseService.java
│ ├── LearningService.java
│ └── ApprovalService.java
│
├── repository
│ ├── UserRepository.java
│ ├── ContentRepository.java
│ ├── CourseRepository.java
│ └── ...
│
├── mapper
│ ├── ContentMapper.java
│ ├── CourseMapper.java
│ ├── LearningMapper.java
│ └── SearchMapper.java
│
├── entity
│
│ ├── user
│ │ ├── User.java
│ │ └── Role.java
│ │
│ ├── content
│ │ ├── Content.java
│ │ ├── ContentVersion.java
│ │ ├── TextContent.java
│ │ ├── VideoContent.java
│ │ ├── QuizContent.java
│ │ └── Attachment.java
│ │
│ ├── course
│ │ ├── Course.java
│ │ ├── CourseVersion.java
│ │ ├── Section.java
│ │ ├── Lesson.java
│ │ └── LessonContent.java
│ │
│ └── learning
│ ├── Enrollment.java
│ └── LearningHistory.java
│
├── dto
│
│ ├── content
│ ├── course
│ ├── learning
│ └── user
│
├── form
│
│ ├── ContentCreateForm.java
│ ├── CourseCreateForm.java
│ └── QuizCreateForm.java
│
├── search
│
│ ├── ContentSearchCondition.java
│ └── CourseSearchCondition.java
│
└── exception
├── BusinessException.java
└── GlobalExceptionHandler.java
|
2. JPA Entity設計方針
今回のシステムでは、
JPA担当
を担当します。
MyBatis担当
| 一覧表示
検索
ツリー取得
集計
ランキング
ダッシュボード
|
を担当します。
3. User Entity
Spring Securityの中心になります。
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
26
27
28
29 | @Entity
@Table(name="users")
@Getter
@Setter
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String email;
private String password;
private boolean enabled;
@CreatedDate
private LocalDateTime createdAt;
}
|
Role
1
2
3
4
5
6
7
8
9
10
11
12 | @Entity
public class Role {
@Id
private Long id;
private String code;
}
|
UserとRoleは多対多です。
4. Content Entity
教材ライブラリの中心です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | @Entity
public class Content {
@Id
private Long id;
@Enumerated(EnumType.STRING)
private ContentType type;
@ManyToOne
private User author;
@OneToOne
private ContentVersion currentVersion;
}
|
ContentType
| public enum ContentType {
VIDEO,
TEXT,
QUIZ,
FILE
}
|
5. ContentVersion
公開管理の中心です。
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 | @Entity
public class ContentVersion {
@Id
private Long id;
private Integer versionNo;
private String title;
private String summary;
@Enumerated(EnumType.STRING)
private ContentStatus status;
private LocalDateTime publishedAt;
}
|
ContentStatus
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | public enum ContentStatus {
DRAFT,
REVIEWING,
APPROVED,
PUBLISHED,
REJECTED,
ARCHIVED
}
|
6. 詳細コンテンツ
Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | @Entity
public class TextContent {
@Id
private Long id;
@OneToOne
private ContentVersion version;
@Lob
private String body;
private String format;
}
|
Video
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | @Entity
public class VideoContent {
@Id
private Long id;
@OneToOne
private ContentVersion version;
private String videoType;
private String videoId;
private String videoUrl;
}
|
Quiz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | @Entity
public class QuizContent {
@Id
private Long id;
@OneToOne
private ContentVersion version;
private String question;
private boolean multipleAnswer;
private Integer answerCount;
}
|
7. Attachment
共通ファイル管理です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | @Entity
public class Attachment {
@Id
private Long id;
@ManyToOne
private ContentVersion version;
private String originalName;
private String storagePath;
private String mimeType;
}
|
8. Course側 Entity
Course
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | @Entity
public class Course {
@Id
private Long id;
private String title;
@ManyToOne
private User teacher;
@OneToOne
private CourseVersion currentVersion;
}
|
CourseVersion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | @Entity
public class CourseVersion {
@Id
private Long id;
private Integer versionNo;
private String description;
private String status;
}
|
Section
無限階層です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | @Entity
public class Section {
@Id
private Long id;
@ManyToOne
private CourseVersion courseVersion;
@ManyToOne
private Section parent;
private String title;
}
|
Lesson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | @Entity
public class Lesson {
@Id
private Long id;
@ManyToOne
private Section section;
private String title;
}
|
LessonContent
教材配置テーブルです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | @Entity
public class LessonContent {
@Id
private Long id;
@ManyToOne
private Lesson lesson;
@ManyToOne
private Content content;
private Integer displayOrder;
}
|
9. Repository設計
ContentRepository
| public interface ContentRepository
extends JpaRepository<Content,Long>{
}
|
用途:
10. MyBatis Mapper設計
例えば教材一覧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | @Mapper
public interface ContentMapper {
List<ContentListDto> search(
ContentSearchCondition condition
);
ContentDetailDto findDetail(
Long id
);
}
|
SQLが複雑になる部分はMyBatisです。
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | SELECT
c.id,
cv.title,
u.name,
COUNT(lc.id)
FROM content c
JOIN content_version cv
JOIN users u
LEFT JOIN lesson_content lc
GROUP BY c.id
|
11. DTO設計
画面用はEntityを直接渡しません。
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | @Data
public class ContentListDto {
private Long id;
private String title;
private String type;
private String authorName;
private String status;
}
|
入力専用です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | @Data
public class TextContentCreateForm {
@NotBlank
private String title;
@NotBlank
private String body;
private String format;
}
|
13. Service責務
ここが一番重要です。
ContentService
担当:
例:
| @Transactional
public void requestReview(Long contentId){
ContentVersion version =
repository.findCurrentVersion(contentId);
version.changeStatus(REVIEWING);
}
|
CourseService
担当:
-
コース作成
-
セクション管理
-
レッスン配置
-
教材配置
ApprovalService
担当:
14. Controller構成
教師
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | /teacher/content
GET /list
GET /create
POST /create
POST /{id}/review
/teacher/course
GET /list
POST /create
|
管理者
| /admin/approval
GET
POST /approve
POST /reject
|
受講者
| /student/course
GET /list
/student/learning
GET /lesson/{id}
POST /complete
|
15. Spring Security権限
Role
| ROLE_ADMIN
ROLE_TEACHER
ROLE_MENTOR
ROLE_STUDENT
|
制御例:
| @PreAuthorize(
"hasRole('TEACHER')")
@PostMapping("/content/create")
|
現時点の完成アーキテクチャ
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
26
27
28
29
30
31
32
33
34
35
36 | 教師
↓
ContentService
↓
ContentRepository(JPA)
↓
ContentVersion
↓
ApprovalService
↓
公開
受講者
↓
CourseMapper(MyBatis)
↓
DTO
↓
Thymeleaf
|
ここまでで Spring Boot実装開始可能な設計レベルになりました。
次の段階では、いよいよ実装に近づけて、
第5段階:MySQL DDL設計 + ER図 + Entity完全版
を作成します。
内容は以下になります。
-
全テーブルDDL
-
PK/FK設計
-
Enum値一覧
-
Index設計
-
JPA Entity完全コード
-
MyBatis Mapper対象一覧
この段階まで進めると、そのままSpring Bootプロジェクトを作成できます。