Spring Boot 3 を使用してアプリを作成してみた【Microservices】

## プロジェクトを作成及び環境を整える

①gatewayプロジェクトを作成する

Project: Gradle
Language: Java
SpringBoot: 3.2.3

Group: com.alibou
Artifact: gateway   config server　discovery
Name : gateway   config server  discovery
Description: Demo project for Spring Boot
Package name: com.alibou.gateway  com.alibou.configserver  com.alibou.discovery
jar 17

Config Client
Eureka Discovery Client
Gateway
Spring Boot Actuator

Config server

②pom.xmlを作成する

③configserverプロジェクトを作成する

Project: Maven
Language: Java
SpringBoot: 3.2.3

Group: com.alibou
Artifact: config-server
Name : config-server
Description: Demo project for Spring Boot
Package name: com.alibou.configserver
jar 17

Config server

④discoveryプロジェクトを作成する

Project: Maven
Language: Java
SpringBoot: 3.2.3

Group: com.alibou
Artifact: discovery
Name : discovery
Description: Demo project for Spring Boot
Package name: com.alibou.discovery
jar 17

Config Client
Eureka Discovery Client
Spring Boot Actuator

Config server

## Discovery Serverを実装する
① main/resouces/application.propertiesの名前をapplication.ymlに変更し、編集する

```application.yml
eureka:
instance:
hostname: localhost
client:
  register-with-eureka: false
  fetch-register: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server: port:8761

spring:
  config:
    import: optinal:configserver:http://localhost:8888
```

② com/alibou/discovery/DiscoveryAppication.javaを編集する

```com.alibou.discovery/DiscoveryAppication.java
package com.alibou.discovery;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableEurekaSever
@SpringBootApplication
public class DiscoveryApplication {

	public static void main(String[] args) {
		SpringApplication.run(DiscoveryApplication.class, args);
	}
}

```
③studentプロジェクトを作成する

Project: Maven
Language: Java
SpringBoot: 3.2.3

Group: com.alibou
Artifact: student
Name : student
Description: Demo project for Spring Boot
Package name: com.alibou.student
jar 17

PostgreSQL Driver
Lombok
Spring Data JPA
Spring Web
Config Client
Eureka Discovery Client
Spring Boot Actuator

④main/resouces/application.propertiesの名前をapplication.ymlに変更し、編集する

```application.yml
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:8888/eureka

server:
port:8090
spring:
  application:
    name: students
  config:
    import: optinal:configserver:http://localhost:8888
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/students
    username: username
    password: password
  jpa:
    hibernate:
      ddl-auto:create
    database: postgresql
    database-platform: org.hibernate.dialect.PostgreSQLDialect
```
⑤com/alibou/studentにclassで Student.javaを作成する

```com/alibou/student/Student.java
package com.alibou.student;

import jakarta.persistence.Entity;
import lombok.*;

@Entity
@Getter
@setter
@AllArgesConstructor
@NoArgesConstructor
@Builder
public class Student {

    @Id
    private Integer id;
    private String firstname;
    private String lastname;
    private String email;
    private Integer schoolId;
}
```
⑥com/alibou/studentにinterfaceで StudentRepository.javaを作成する

```StudentRepository.java
package com.alibou.student;

import org.springframework.data.jpa.repository.JpaRepository;

public interface StudentRepository extends JpaRepository<Student, Integer> {

}
```

⑦com/alibou/studentにclassで StudentService.javaを作成する

```StudentService.java
package com.alibou.student;

import lombok.RequiredArgsConstrucutor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstrucutor
public class StudentService {

    private final StudentRepository repository;

    public void saveStudent(Student student) {
        repository.save(student);

    }

    public List<Student> findAllStudents(){
        return repository.findAll()
    }
}
```

⑧ com/alibou/studentに StudentController.javaを作成する

```StudentController.java
package com.alibou.student;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RestController
@RequestMapping("/api/v1/students")
@RequiredArgsConstructor
public class StudentController {

    private StudentService service;

    @PostMapping
    @ResponseStatus(HttpStaus.CREATED)
    public void save(
            @RequestBody Student student) {

        service.saveStudent(student);
    }

    @GetMapping
    public ResponseEntity<List<Student>> findAllStudents() {
        return ResponseEntity.ok(service.findAllStudents());
    }

}
```

⑨studentプロジェクトを作成する

Project: Maven
Language: Java
SpringBoot: 3.2.3

Group: com.alibou
Artifact: school
Name : school
Description: Demo project for Spring Boot
Package name: com.alibou.school
jar 17

PostgreSQL Driver
Lombok
Spring Data JPA
Spring Web
Config Client
Eureka Discovery Client
Spring Boot Actuator
OpenFeign

⑩studentのapplication.ymlをコピーしにschoolのapplication.ymlに変更し、貼り付ける

```application.yml
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:8888/eureka

server:
port:8070
spring:
  application:
    name: schools
  config:
    import: optinal:configserver:http://localhost:8888
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/schools
    username: username
    password: password
  jpa:
    hibernate:
      ddl-auto:create
    database: postgresql
    database-platform: org.hibernate.dialect.PostgreSQLDialect
```
⑪studentプロジェクトのjava/com/alibou/student/Student.javaをコピーして
schoolプロジェクトのjava/com/alibou/schoolにSchool.javaに作成及び貼り付けて編集する。

```School.java
package com.alibou.school;

import jakarta.persistence.Entity;
import lombok.*;

@Entity
@Getter
@setter
@AllArgesConstructor
@NoArgesConstructor
@Builder
public class School {

    @Id
    private Integer id;
    private String name;
    private String email;
}
```
⑫studentプロジェクトのjava/com/alibou/student/StudentRepository.javaをコピーしてschoolプロジェクトのjava/com/alibou/schoolにSchoolRepository.javaに作成及び貼り付けて編集する。

```SchoolRepository.java
package com.alibou.school;

import org.springframework.data.jpa.repository.JpaRepository;

public interface SchoolRepository extends JpaRepository<School, Integer> {

}
```
⑬studentプロジェクトのjava/com/alibou/student/StudentService.javaをコピーしてschoolプロジェクトのjava/com/alibou/schoolにSchoolService.javaに作成及び貼り付けて編集する。

```SchoolService.java

package com.alibou.school;

import lombok.RequiredArgsConstrucutor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstrucutor
public class SchoolService {

    private final SchoolRepository repository;

    public void saveStudent(School school) {
        repository.save(school);

    }

    public List<School> findAllSchools(){
        return repository.findAll()
    }
}
```
⑭studentプロジェクトのjava/com/alibou/student/StudentController.javaをコピーしてschoolプロジェクトのjava/com/alibou/schoolにSchoolController.javaに作成及び貼り付けて編集する。

```SchoolController.java
package com.alibou.school;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RestController
@RequestMapping("/api/v1/schools")
@RequiredArgsConstructor
public class SchoolController {

    private SchoolService service;

    @PostMapping
    @ResponseStatus(HttpStaus.CREATED)
    public void save(
            @RequestBody School school) {

        service.saveSchool(school);
    }

    @GetMapping
    public ResponseEntity<List<School>> findAllSchools() {
        return ResponseEntity.ok(service.findAllSchools());
    }
}
```
## エンドポイントを作成する
① SchoolController.java内で新しいエンドポイントを作成する

```SchoolController.java
package com.alibou.school;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RestController
@RequestMapping("/api/v1/schools")
@RequiredArgsConstructor
public class SchoolController {

    private SchoolService service;

    @PostMapping
    @ResponseStatus(HttpStaus.CREATED)
    public void save(
            @RequestBody School school) {

        service.saveSchool(school);
    }

    @GetMapping
    public ResponseEntity<List<School>> findAllSchools() {
        return ResponseEntity.ok(service.findAllSchools());
    }

    @GetMapping{"/with-students/{school-id}"}

    public ResponseEntity<FullScholResponse> findAllSchools(
        ＠PathVariable("school-id")Integer schoolId
    ){
        return ResponseEntity.ok(service.findSchoolsWithStudents(schoolId));
    }
}
```
②SchoolServer.javaを編集する

```SchoolServer.java
package com.alibou.school;

import lombok.RequiredArgsConstrucutor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstrucutor
public class SchoolService {

    private final SchoolRepository repository;

    public void saveStudent(School school) {
        repository.save(school);

    }

    public List<School> findAllSchools() {
        return repository.findAll();

    }

    public FullschoolResponse findSchoolsWithStudents(Integer schoolId) {
        return null;

    }
}
```
③FullSchollResponse.javaを作成する

```FullSchollResponse.java
package com.example.school;

@AllArgsConstructor
@Builder
@Getter
@NoArgsConstructor
@Setter

public class FullSchollResponse {

    private String name;
    private String email;
    List<Student> students;

}
```

④ Student.javaを作成する

```Student.java
package com.example.school;

import lombok.*;

@Entity
@Getter
@setter
@AllArgesConstructor
@NoArgesConstructor
@Builder
public class Student {

    private Integer firstname;
    private String lastname;
    private String email;

}
```

⑤ SchoolService.javaを編集する

```SchoolService.java
package com.alibou.school;

import lombok.RequiredArgsConstrucutor;
import org.springframework.stereotype.Service;

import com.alibou.school.School;

@Service
@RequiredArgsConstrucutor
public class SchoolService {

    private final SchoolRepository repository;

    public void saveStudent(School school) {
        repository.save(school);

    }

    public List<School> findAllSchools() {
        return repository.findAll();

    }

    public FullschoolResponse findSchoolsWithStudents(Integer schoolId) {
        var school = repository.findAllById(schoolId)
                .orElse(
                        School.builder()
                                .name("NOT_FOUND")
                                .email("NOT_FOUND")
                                .build());
        var students = null;// find all the students from the student micro service
        return null;

    }
}
```
⑥ StudentController.javaを編集する

```StudentController.java
package com.alibou.student;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

@RestController
@RequestMapping("/api/v1/students")
@RequiredArgsConstructor
public class StudentController {

    private StudentService service;

    @PostMapping
    @ResponseStatus(HttpStaus.CREATED)
    public void save(
            @RequestBody Student student) {

        service.saveStudent(student);
    }

    @GetMapping
    public ResponseEntity<List<Student>> findAllStudents() {
        return ResponseEntity.ok(service.findAllStudents());
    }

    @GetMapping("/school/{school-id}")
    public ResponseEntity<List<Student>> findAllStudents(
            @PathVariable("school-id") Integer schoolId) {
        return ResponseEntity.ok(service.findAllStudentsBySchool(schoolId));
    }
}
```
⑦StudentService.javaを編集する

```StudentService.java
package com.alibou.student;

import lombok.RequiredArgsConstrucutor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstrucutor
public class StudentService {

    private final StudentRepository repository;

    public void saveStudent(Student student) {
        repository.save(student);

    }

    public List<Student> findAllStudents() {
        return repository.findAll();
    }

    public List<Student> findAllStudentsBySchool(Integer schookId) {
        return repository.findAllBySchoolId(schoolId);
    }
}
```

⑧StudentRepository.javaを編集する

```StudentRepository.java
package com.alibou.student;

import org.springframework.data.jpa.repository.JpaRepository;

public interface StudentRepository extends JpaRepository<Student, Integer> {

    List<Student> findAllBySchoolId(Integer schoolId);
}
```

⑨ client/StudentClient.javaを編集する

```StudentClient.java

```
⑩ resouces/application.ymlを編集する

```resouces/application.yml

//省略
application:
    confin:
      students-url: http://localhost:8090/api/v1/students

```
⑪ SchoolService.javaを編集する

```SchoolService.java
package com.alibou.school;

import lombok.RequiredArgsConstrucutor;
import org.springframework.stereotype.Service;

import com.alibou.school.School;
import com.example.client.StudentClient;

@Service
@RequiredArgsConstrucutor
public class SchoolService {

    private final SchoolRepository repository;

    private StudentClient client

    public void saveStudent(School school) {
        repository.save(school);

    }

    public List<School> findAllSchools() {
        return repository.findAll();

    }

    public FullschoolResponse findSchoolsWithStudents(Integer schoolId) {
        var school = repository.findAllById(schoolId)
                .orElse(
                        School.builder()
                                .name("NOT_FOUND")
                                .email("NOT_FOUND")
                                .build());
        var students = client.findAllStudentsBySchool(schoolId);
        return FullSchoollResponse.builder()
                .name(school.getName())
                .email(school.getEmail())
                .students(students)
                .build();
    }
}
```

⑫SpringApplication.javaを編集する

```SpringApplication.java
package com.example.school;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableFeignClients
@SpringBootApplication
public class SchoolApplication {

	public static void main(String[] args) {
		SpringApplication.run(SchoolApplication.class, args);
	}
}
```
## APIGATEWAYを使用してアクセスする

① gateway/resouces/application.properties⇨application.ymlに変更する

```application.yml
eureka:
    client:
    register-with-eureka: false
server:
port: 8222
spring:
    application:
    name: gateway
config:
import: optional:configserver:http://localhost:8888
cloud:
    gateway:
    discovery:
    locator:
    enabled: true
routes:
    - id: students
      uri: http://localhost:8090
      predicates:
    - Path=/api/v1/students/**
    - id: students
      uri:http://localhost:8070
      predicates:
    - Path=/api/v1/schools/**
```

② school/resouces/application.ymlを編集する

```application.yml
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:8888/eureka

server:
port:8070
spring:
  application:
    name: schools
  config:
    import: optinal:configserver:http://localhost:8888
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/schools
    username: username
    password: password
  jpa:
    hibernate:
      ddl-auto:create
    database: postgresql
    database-platform: org.hibernate.dialect.PostgreSQLDialect

  application:
    confin:
      students-url: http://localhost:8222/api/v1/students
```

③micro-services/resouces/application.propertiesをapplication.ymlに編集する

```application.yml
server:
  port: 8888

spring:
  profiles:
    active: native
  application:
    name: config-server
  cloud:
    config:
      server:
        native:
          search-locations: classpath:/configurations
```

④ConfigServerApplication.javaを編集する

```ConfigServerApplication.java
package com.alibou.configserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnabaleConfigServer
@SpringBootApplication
public class ConfigServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConfigServerApplication.class, args);
	}
}
```
⑤micro-services/resouces/configurations/schools.ymlを作成する

```
spring:
  application:
    name: schools
  confing:
    import: optional:configserver:http://localhost:8888
````
⑥micro-services/resouces/configurationsにstudents.ymlを作成する

```students.yml
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:8888/eureka

server:
port: 8090
spring:
  application:
    name: students

  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/students
    username: username
    password: password
  jpa:
    hibernate: ddl-auto:create
    database: postgresql
    database-platform: org.hibernate.dialect.PostgreSQLDialect
```

⑦student/resouces/application.ymlを編集する

```application.yml
spring:
  application:
    name: students
  config:
    import: optinal:configserver:http://localhost:8888
```

⑧micro-services/resouces/configurationsにapplication.ymlを編集する

```micro-services/resouces/configurations/application.yml
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/
server:
  port: 8761
spring:
  config:
    import: optional:configserver:http://localhost:8888
  application:
    name: discoveryd
```

⑨micro-services/resouces/configurationsにdiscovery.ymlを作成する

```discovery.yml
spring:
  config:
    import: optinal:configserver:http://localhost:8888
  application:
    name: discovery
```
⑩discovery/resource/application.ymlを編集する

```discovery/resource/application.yml
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
  port: 8761
spring:
  application:
    name: discovery
  config:
    import: optinal:configserver:http://localhost:8888

```

## 参考サイト
[Microservices tutorial with Spring boot 3 | Full course](https://www.youtube.com/watch?v=KJ0cSvYj41c)