概要 / 説明
Kotlinでサーバーサイドアプリケーションを作ってきましたが、まだきちんとした構成にはなっていない状態です。
「ことりんと一緒 Springもね - 2. RestController と Data Class」 でコントローラクラスを作り、直接データを扱うような構成のままです。
そこで、少しずつ変更を行っていきます。
まず、ここでは「サービス」層を考えていきます。
Webアプリケーションでは大きく分けて次のような機能が必要となります。
- リクエスト/レスポンスの処理
- アプリケーションロジックの処理
- データの処理
今まではこれらの機能を単一レイヤで担当していました。
このままの構成だと、扱うロジックが増え、データモデルが複雑になってくると、
1つのクラスが巨大になり開発/保守効率が悪くなってしまいます。
それぞれの機能を分離して、複数のレイヤに分けていきます。
さて、作っていたクラスが RestController ということで、
本来はリクエスト/レスポンスを扱うことを責務とします。
そこで、それ以外の処理を切り出し、それを サービス とします。
前提 / 環境
ランタイムバージョン
- Kotlin : 1.3.0
- SpringBoot : 2.1.0.RELEASE
Spring Dependencies
- Web
- Actuator
開発環境
- OS : Mac
- IDE : IntelliJ IDEA
- Build : Gradle
手順 / 解説
変更前のコントローラクラス
元々は以下のようにコントローラクラスの中でデータの生成など、リクエスト/レスポンスの処理以外のことを行っていました。
@RestController
@RequestMapping("/messages")
class SimpleController {
    @GetMapping
    fun getMessages() : List<Message> {
        return listOf(
                Message(
                        UUID.randomUUID().toString(),
                        "First Message",
                        "This is a 1st message on ${getDate()}."
                ),
                Message(UUID.randomUUID().toString(),
                        "Second Message",
                        "This is a 2nd message on ${getDate()}."
                )
        )
    }
    private fun getDate() : String {
        val simpleDateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
        return simpleDateFormat.format(Date())
    }
}
サービスクラス
元々コントローラクラスに実装していた内容を以下のようにサービスクラスに移します。
@Service("Message Service")
class MessageService {
    fun getMessages() : List<Message> {
        return listOf(
                Message(
                        UUID.randomUUID().toString(),
                        "First Message",
                        "This is a 1st message on ${getDate()}."
                ),
                Message(UUID.randomUUID().toString(),
                        "Second Message",
                        "This is a 2nd message on ${getDate()}."
                )
        )
    }
    private fun getDate() : String {
        val simpleDateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
        return simpleDateFormat.format(Date())
    }
}
サービスクラスには @Service アノテーションを付与します。
これによりコントローラクラスの中で Dependency Injection により利用できるようになります。
@Autowired
private lateinit var service: MessageService
lateinit キーワードは、インスタンスを初期化せずに宣言するときに使用します。
変更後のコントローラクラス
サービスクラスを使用するコントローラクラスでは、リクエスト/レスポンスに関する処理だけを担当するようにします。
以下のようにサービスクラスで実装している処理を呼び出すように変更を行います。
@RestController
@RequestMapping("/messages")
class MessageController() {
    @Autowired
    private lateinit var service: MessageService
    @GetMapping
    fun getMessages() = service.getMessages()
}
まとめ / 振り返り
責務ごとにレイヤを分けてアプリケーションを開発するのことは、今や定石ですね。
まだ、この状態ではサービスレイヤからデータ処理を直接行うようになっているので、
次はその部分を分離していきます。
