Spring BootとJPAでREST APIを実装する(インフラ層編)

[Spring Boot + MySQLでシンプルなWeb REST APIサーバを実装する - Qiita](https://qiita.com/YutaKase6/items/007f6b7d12d342c1330c)

# Outline
以下の設計に従って、インフラ層を実装する。
[Spring Bootで実装するWebAPIのアーキテクチャを考える - Qiita](https://qiita.com/YutaKase6/items/7d88fa23f81366905270)

以下の3クラスを作成する。

- UserEntity.java
- UserJpaRepository.java
- UserRepository.java

```
├── infrastructure
│   ├── entity
│   │   └── UserEntity.java
│   └── repository
│       ├── UserJpaRepository.java
│       └── UserRepositoryImpl.java
```

# UserEntity.java
MySQLのレコードがマッピングされるクラス。
Domain Object(User.java)と双方向に変換するメソッドを定義。
 
```UserEntity.java
package com.example.springapi.infrastructure.entity;

import com.example.springapi.domain.object.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

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

/**
 * RDBレコードのマッピング用クラス
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "test_users")
public class UserEntity {
    @Id
    @Column(name = "id")
    private String id;

    @Column(name = "value")
    private String value;

    /**
     * ドメインオブジェクトからインスタンスを生成
     *
     * @param user ドメインオブジェクト
     * @return UserEntity
     */
    public static UserEntity build(User user) {
        return UserEntity.builder()
                .id(user.getId())
                .value(user.getValue())
                .build();
    }

    /**
     * ドメインオブジェクトへ変換
     *
     * @return ドメインオブジェクト
     */
    public User toDomainUser() {
        return User.builder()
                .id(this.id)
                .value(this.value)
                .build();
    }
}
```
## ※ JPAアノテーション
### @Entity
これでJPAのEntity(マッピングされるクラス)ってことを表現する。
### @Table
テーブル名を指定する。
### @id
プライマリキーであることを指定する。
複合キーの場合は書き方が変わる。
### @Column
テーブルの列名を指定する。

## ※ Lombokアノテーション
### @NoArgsConstructor
引数なしのコンストラクタを自動で作成する。
JPAのEntityのライフサイクル上、引数なしのコンストラクタが必要。
引数なしのコンストラクタが存在するような構成ならば不要。(finalなフィールドが無い場合など。@Builder使うなら絶対必要)
### @AllArgsConstructor 
すべてのフィールドを引数に取るコンストラクタを作成する。
NoArgsConstructorを指定した場合、こちらも指定しないと、@Builderが動かない(@Builderではすべてのフィールドを引数にとるコンストラクタを自動で生成するが、xxxArgsConstructorがついてるクラスに対しては、コンストラクタを作成しない。そして動かない。)

# UserJpaRepository.java
JPAの実装を利用するために必要なインタフェース。
JpaRepositoryを継承し、Entityクラスと、プライマリキーの型を指定する。
実行時にはSimpleJpaRepositoryというspring-data-jpaが用意している標準実装がDIコンテナによって注入されるため、実装クラスを記述する必要はない。

```UserJpaRepository.java
package com.example.springapi.infrastructure.repository;

import com.example.springapi.infrastructure.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * JPAを利用するためのインタフェース
 */
public interface UserJpaRepository extends JpaRepository<UserEntity, String> {
}
```

# UserRepositoryImpl.java
ドメイン層で定義したUserRepositoryインタフェースの実装クラス。
実際の処理はSimpleJpaRepositoryが行う。

```UserRepositoryImpl.java
package com.example.springapi.infrastructure.repository;

import com.example.springapi.domain.object.User;
import com.example.springapi.domain.repository.UserRepository;
import com.example.springapi.infrastructure.entity.UserEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Optional;

/**
 * 永続化の実装クラス
 * ドメインオブジェクトをEntityに変換してJPAをラップする
 */
@Repository
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepository {

    @NonNull
    private final UserJpaRepository userJpaRepository;

    /**
     * {@inheritDoc}
     */
    @Override
    public Optional<User> findById(String id) {
        return this.userJpaRepository.findById(id)
                .map(UserEntity::toDomainUser);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public User save(User user) {
        return this.userJpaRepository.save(UserEntity.build(user))
                .toDomainUser();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteById(String id) {
        this.userJpaRepository.deleteById(id);
    }
}
```

### @Repository
@Compornentのエイリアス。
@Serviceと一緒。