コンテンツにスキップ

ありがとうございます。

ここから 第17段階:共通基盤・運用機能設計(例外処理・ログ・通知・バッチ・AOP整理) に進みます。

ここまでで教材管理システムとして主要機能は揃いました。

現在:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
教師
 └ 教材作成
      └ 審査
          └ 公開

受講者
 └ 受講
      └ 進捗管理

管理者
 └ ユーザー管理
 └ 権限管理
 └ 承認管理

共通
 └ 監査ログ
 └ ファイル管理

まで完成しています。

第17段階では、本番運用に耐えられるSpringアプリケーション基盤を設計します。


第17段階 全体構成

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

                         |

        +----------------+----------------+

        Exception              Logging

        Handler                AOP


        Validation             Transaction


        Notification           Batch


                         |

                    Database

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
25
26
27
28
29
30
31
com.example.lms

├ config
│
├ security
│
├ common
│  ├ exception
│  ├ message
│  ├ util
│  └ audit
│
├ domain
│  ├ user
│  ├ content
│  ├ course
│  └ learning
│
├ application
│  ├ service
│  └ usecase
│
├ infrastructure
│  ├ repository
│  ├ mapper
│  └ storage
│
└ web
   ├ controller
   ├ form
   └ dto

2. 例外処理設計

目的

現在:

1
Whitelabel Error Page

になる問題を解消します。


3. 例外クラス設計

配置:

1
common.exception

BusinessException

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Getter
public class BusinessException
        extends RuntimeException {


    private final String code;


    public BusinessException(
            String code,
            String message){

        super(message);

        this.code=code;

    }

}

4. 例外種類

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

├ BusinessException
│
├ ResourceNotFoundException
│
├ UnauthorizedException
│
├ ForbiddenException
│
└ SystemException

5. ResourceNotFoundException

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class ResourceNotFoundException
        extends BusinessException {


    public ResourceNotFoundException(
            String message){

        super(
            "NOT_FOUND",
            message
        );

    }

}

6. ControllerAdvice

配置:

1
2
3
common.exception

└ GlobalExceptionHandler

GlobalExceptionHandler.java

 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
@ControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler {


    private final MessageService messageService;


    @ExceptionHandler(
        ResourceNotFoundException.class
    )
    public String notFound(
            ResourceNotFoundException e,
            Model model){


        model.addAttribute(
            "message",
            e.getMessage()
        );


        return "error/404";

    }



    @ExceptionHandler(
        BusinessException.class
    )
    public String business(
            BusinessException e,
            Model model){


        model.addAttribute(
            "message",
            e.getMessage()
        );


        return "error/error";

    }



    @ExceptionHandler(Exception.class)
    public String system(
            Exception e){


        return "error/system";

    }

}

7. エラー画面

構成:

1
2
3
4
5
6
7
8
9
templates

└ error

   ├ 404.html

   ├ error.html

   └ system.html

404.html

表示:

1
2
3
教材が存在しません。

[戻る]

8. バリデーション共通化

現在:

1
2
@NotBlank
private String title;

ですが、メッセージを一元管理します。


9. messages.properties

配置:

1
2
3
resources

└ messages.properties

内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
content.title.required=
教材タイトルを入力してください


content.summary.required=
概要を入力してください


file.required=
ファイルを選択してください

Form:

1
2
3
4
@NotBlank(
message="{content.title.required}"
)
private String title;

10. メッセージ管理Service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@Service
@RequiredArgsConstructor
public class MessageService {


private final MessageSource source;


public String get(
        String code){

    return source.getMessage(
        code,
        null,
        Locale.JAPAN
    );

}

}

11. Spring AOP整理

現在:

1
AuditAspect

があります。

さらに共通化します。


構成:

1
2
3
4
5
6
7
common.aop

├ AuditAspect

├ LoggingAspect

└ TransactionAspect

12. LoggingAspect

目的:

Service実行時間計測。


 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
@Aspect
@Component
@Slf4j
public class LoggingAspect {


@Around(
"execution(* com.example.lms.application..*(..))"
)
public Object log(
        ProceedingJoinPoint joinPoint)
        throws Throwable {


long start =
System.currentTimeMillis();


try{

return joinPoint.proceed();


}finally{


long time =
System.currentTimeMillis()
-start;


log.info(
"{} {}ms",
joinPoint
.getSignature()
.getName(),
time
);


}


}

}

13. トランザクション設計

ルール:

Controller

1
@Transactional禁止

Service

1
@Transactional

例:

1
2
3
4
5
6
@Service
@Transactional
public class ContentService {


}

14. Read Only Transaction

検索処理:

1
2
3
4
5
6
@Transactional(
readOnly=true
)
public List<ContentDto> search(){

}

メリット:

  • 不要なflush防止

  • 性能向上


15. ログ設計

利用:

Spring Boot標準

1
2
3
SLF4J
+
Logback

構成:

1
2
3
4
5
6
7
logs

├ application.log

├ error.log

└ audit.log

16. logback-spring.xml

 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
<configuration>


<appender name="FILE"
class="ch.qos.logback.core.FileAppender">

<file>
logs/application.log
</file>


<encoder>

<pattern>
%d %-5level %msg%n
</pattern>

</encoder>

</appender>


<root level="INFO">

<appender-ref ref="FILE"/>

</root>


</configuration>

17. メール通知設計

対象:

  • 教材承認完了

  • 差戻し

  • 受講完了

  • パスワード変更


構成:

1
2
3
4
5
6
7
notification

├ NotificationService

├ MailService

└ Template

18. Notificationテーブル

履歴を保持します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
CREATE TABLE notifications (

id BIGINT AUTO_INCREMENT PRIMARY KEY,

user_id BIGINT,

type VARCHAR(50),

title VARCHAR(200),

message TEXT,

read_flag BOOLEAN,

created_at DATETIME

);

19. Notification Entity

 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
@Entity
@Getter
@Setter
public class Notification {


@Id
@GeneratedValue
private Long id;


@ManyToOne
private User user;


private String type;


private String title;


@Column(columnDefinition="TEXT")
private String message;


private Boolean readFlag;


private LocalDateTime createdAt;


}

20. 非同期通知

メール送信は時間がかかるため、

1
2
3
4
@Async
public void sendMail(){

}

を利用します。


設定:

1
2
3
4
5
6
@EnableAsync
@Configuration
public class AsyncConfig {


}

21. バッチ処理設計

Spring Batchを利用します。

用途:

  • 古い監査ログ削除

  • 期限切れファイル削除

  • 受講進捗集計

  • リマインドメール


構成:

1
2
3
4
5
6
7
batch

├ AuditCleanupJob

├ ProgressSummaryJob

└ ReminderMailJob

22. 定期処理

例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Component
public class ReminderTask {


@Scheduled(
cron="0 0 9 * * MON"
)
public void execute(){

}

}

23. 定期処理例

毎週月曜日9時:

1
2
3
4
5
6
7
8
9
7日間ログインなし

↓

受講者へ通知

↓

メンターへ通知

24. セキュリティログ

追加推奨:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CREATE TABLE security_logs (

id BIGINT AUTO_INCREMENT PRIMARY KEY,

user_id BIGINT,

event_type VARCHAR(50),

ip_address VARCHAR(50),

created_at DATETIME

);

記録:

1
2
3
4
5
LOGIN_SUCCESS

LOGIN_FAILURE

PASSWORD_CHANGE

25. 第17段階終了後の構成

 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
                 Spring Boot


 Controller

     |

 Service

     |

 +---+---+---+

AOP Exception Transaction


     |

Repository / Mapper


     |

Database



Storage

     |

Cloudflare R2

第17段階終了時点

追加された機能:

✅ 共通例外処理
✅ エラー画面
✅ Validationメッセージ管理
✅ AOPログ処理
✅ 監査処理整理
✅ トランザクション方針
✅ ログ設計
✅ 通知設計
✅ バッチ設計
✅ セキュリティログ設計


次の 第18段階 では、画面設計とThymeleaf実装方針に進みます。

内容:

  1. 共通レイアウト設計

  2. Bootstrap5設計

  3. 管理者画面

  4. 教師画面

  5. 受講者画面

  6. Thymeleaf Fragment設計

  7. Form入力画面

  8. 一覧・詳細・編集画面パターン

  9. ページネーション

  10. Ajax化する部分

を設計します。