Angular, Spring, PostgreSQL(13)Springの サービスを作る

前回(No.12の記事)は、コントローラーを作成し、ブラウザからのリクエストに対して、テーブル検索をして、値を返せるようにしました。

一応、コントローラーとレポジトリの連携は出来ました。

ただ、役割的には、コントローラーって「機能系、画面系」の処理をまとめるもので、サービスが「DBとのやりとり、業務処理」をやるところなんだろうなぁ、、、と思っています。

なので今回は、サービスを作って、検索はそこからするようにしてみます。

スポンサーリンク

アプリケーション構造を再確認

サービス(Service)の位置づけを、改めて確認しておきます。


今回作るServiceは、以下のような流れ。

Controllerから呼び出され、検索処理をして、Controllerに結果を返す。

では作っていきましょう♪。

スポンサーリンク

サービスを作る

Serviceは、Controllerから「カテゴリを全検索して、一覧をくれ」とお願いされる想定なので、なかでカテゴリ検索を実行する、以下のようなクラスを作成しました。

・interface「CategoryService」

package com.chankazu.tts.domain.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.chankazu.tts.domain.entity.CategoryEntity;

@Service
public interface CategoryService {

	List<CategoryEntity> findAll();
	CategoryEntity findById(Long id);

}

・implements「CategoryServiceImpl」

package com.chankazu.tts.domain.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.chankazu.tts.domain.entity.CategoryEntity;
import com.chankazu.tts.domain.repository.CategoryRepository;

@Service
public class CategoryServiceImpl implements CategoryService {

	@Autowired
	CategoryRepository categoryRepository;

	public List<CategoryEntity> findAll() {

		List<CategoryEntity> categories = categoryRepository.findAllByOrderByIdAsc();

		if (categories == null) {
                   // エラー処理
は追々
                }
                return categories;
	}

	public CategoryEntity findById(Long id) {

		CategoryEntity category = categoryRepository.findById(id);

		if (category == null) {
                  // エラー処理
は追々
                }
                return category;
	}
}

いろいろ参考にしながら、とりあえず作ってみましたが、いくつか気になることがあったので、ちょっとまとめておきます。

interfaceとimplements

CategoryService(interface)とCategoryServiceImpl(implements)に分けました。

宣言(interface)と実装(implements)を分けると「こんな良いことが」「あんな良いことが」「ありますよ~」という記事をちらちら見かけますが、この程度のアプリでは、それほどメリットはみえませんね。

実はSpringがそうしてほしそうですが、今はそこを深追いするのもしんどいので、当面はお約束に従うことにします。

@Service?、@Autowired?

薄々気付いてはいました。「最近のプログラムは、”new”しないな~」と。

旧い人間なので、しっかり・がっちり ”new” して、あちこち繋ぎとめておいたほうが落ち着くのですが、「もうこの際、そこはフレームワークに任せましょう」というのが今の流れなのかな?。

@Serviceとか @Componentをつけて、「こいつを管理してください」とお願いし、@Autowired で「いいタイミングでnewしといてください」とお願いする感じですね。

雑な理解では後でまた悩むことになるかもしれませんが、悩む時が来ないと結局、深く納得はできないわけなので、いったん良しとします。

コントローラーを見直す

Serviceの実装にあわせて、Controllerも修正を行いました。

ポイントがいくつかあります。

package com.chankazu.tts.app.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.chankazu.tts.app.dto.CategoryDto;
import com.chankazu.tts.domain.entity.CategoryEntity;
import com.chankazu.tts.domain.service.CategoryService;

@RestController
class CategoryController {

    @Autowired
    CategoryService categoryService;

	// Aggregate root
	// tag::get-aggregate-root[]
	@GetMapping("/categories")
	List<CategoryDto> findAll() {

		List<CategoryEntity> entityList  = categoryService.findAll();
		List<CategoryDto>    dtoList	  = new ArrayList<CategoryDto>();

		for(CategoryEntity entity : entityList){
			dtoList.add(new CategoryDto(entity));
		}
		return dtoList;
	}

	// end::get-aggregate-root[]
	// Single item
	@GetMapping("/categories/{id}")
	CategoryDto findById(@PathVariable Long id) {

		CategoryEntity entity = categoryService.findById(id);
		CategoryDto	 dto = new CategoryDto(entity);
		return dto;

	}
}

CategoryServiceを@Autowired

前回のCategoryControllerは、CategoryRepositoryをもらってくる形で初期化して、その後、CategoryRepository内のメソッドを呼び出すようにしましたが、今回は、CategoryServiceを@Autowired指定して、初期化をお願いする形にしました。まぁ、こっちのほうがシンプル。

DTOへの詰め替え

Serviceのメソッドは、Entity(のリスト)をそのまま返してきます。

Controllerは、それを受け取って、画面側(現状想定しているのは、Angular)に戻す予定なのだけれど、Controllerから画面に情報が遡る時は、Entityの形がそのまま戻っていくのはいまいちだろうと思う(DB上の形が画面までいってしまう)。

なので、Controllerは、サービスから受け取ったEntity(のリスト)を、さらに画面に受け渡す形(dto)に変える必要があるのでは?、と思って、Listを回してEntity→dtoの詰め替えをしています。

けど、なんかもやもやする。。。

①機能・画面系dtoへの詰め替え処理の場所って、コントローラーで良い?

 ※これは、Controller側で良さそうです

②こんな、( for(CategoryEntity entity : entityList) で )ぐるぐる回す(古めかしい)やり方しかない?

 ※Mapperとかが使えるかも?、いつかやってみたい

③そもそも、domein.entity.CategoryEntity と、app.dto.CategoryDtoって現状違いがほぼない。

package com.chankazu.tts.app.dto;

import com.chankazu.tts.domain.entity.CategoryEntity;

public class CategoryDto {

	private  Long id;
	private  String name;

	public CategoryDto(Long id, String name) {
		this.id = id;
		this.name = name;
	}
	public CategoryDto(CategoryEntity entity) {
		this.id = entity.getId();
		this.name = entity.getName();
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
	    this.name = name;
	}

}
package com.chankazu.tts.domain.entity;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "category")
public class CategoryEntity {

	@Id
	private Long id;

	private String name;

	protected CategoryEntity() {}

	public CategoryEntity(Long id, String name) {
		this.id = id;
		this.name = name;
	}
	@Override
	public String toString() {
		return String.format(
		    "Category[id=%d, name='%s']",id, name);
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

 ※微妙~に違いますけどね、分けるほどでもない気がする

このあたり、より「エレガントな」作り方の探求は、継続課題としていこうと思います。

実行してみる

Eclipseから、bootRunしてみます。

「http://localhost:8080/categories」を行うと、

中身は変ったのですが、前回と同様の結果です。問題ないようです。。

次回は画面(Angular)を作る

簡単な、検索指示と検索結果表示を、Angularベースで作ってみます。

あと、そろそろ、コードは、gitに公開したほうが良いかもしれない。

コードの貼り付けが多くなってきた(今後さらに増えると予想)ので、記事に貼るのは伝えたい部分だけにして「後はgitを見てね」のほうが、必要以上に記事が大きくならず、良さそうです。

コメント

タイトルとURLをコピーしました