Post

02 MSA의 Service 패턴

02 MSA의 Service 패턴

1. memo

  • 각기 기능을 하는 Service들을 만들어놓고 필요에 따라서 상호보완으로 작업함.
  • MVC를 만들어서 그것을 기준으로 할경우
    • web에서 action이 발생 -> 서버 다른 AP요청보다는
    • Web에서 비동기방식으로 특정 AP에 요청 구조가 적합.
  • 스프링에서는 Spring Gateway 방식을 사용해서 요청을 뿌려주는 역할로도 사용함.
  • SSR/CSR + 모놀리식/MSA
    • 모놀리식 + SSR이 잘 맞았던 이유는 화면 렌더링 / 비즈니스 로직 / 데이터 접근 과정이 자기 안에서 모든 것이 해결 가능했으나
    • MSA는 SSR 서버로 와도 어차피 다른 서비스에 요청을 해야하기 때문에 차이가 없음.
    • 경계가 사라지고, 영향이 줄어듬!
    • 장애를 줄이자!

2. User Service

1. memo

  • 유저 Service에서는 유저에 대한 정보만 처리함.

2. UserController

1
2
3
4
5
6
7
8
9
10
11
12
13
  //유저 가입
  @PostMapping("/sign-up")
  public ResponseEntity<Void> signUp(@RequestBody SignUpRequestDto signUpRequestDto) {
      userService.signUp(signUpRequestDto);
      return ResponseEntity.noContent().build();
  }

  // 유저 정보 조회
  @GetMapping("{userId}")
  public ResponseEntity<UserResponseDto> getUser(@PathVariable Long userId) {
      UserResponseDto userResponseDto = userService.getUser(userId);
      return ResponseEntity.ok(userResponseDto);
  }

3. UserService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  // 신규가입
  @Transactional
  public void signUp(SignUpRequestDto signUpRequestDto) {
     User user = new User(
             signUpRequestDto.getEmail(),
             signUpRequestDto.getName(),
             signUpRequestDto.getPassword()
     );

     this.userRepository.save(user);
  }

  // 유저정보 return
  public UserResponseDto getUser(Long id) {
      User user = userRepository.findById(id)
              .orElseThrow(() -> new IllegalArgumentException("사용자를 찾을 수 없습니다."));

      return new UserResponseDto(
              user.getUserId(),
              user.getEmail(),
              user.getName()
      );
  }

4. DTO

  • SignUpRequestDto : email / name / password
  • UserResponseDto : email / name / password

5. domain

  • User : userId / email / name/ password

3. board Service

1. memo

  • board Service는 user정보를 가지고 내용을 생성해야하기 때문에, 중간에 UserService와 커넥션이 필요함.
  • 지금 예시에서는 “ RestClient “ 을 사용함
  • 필요에 따라서 여러개 동시에 사용하기도 하고 비즈니스로직에 따라 다름!
  • 굳이 중심이 되는 gateway나 MVC, 웹에 다녀올 필요없음.

2. boardController

1
2
3
4
5
6
7
8
9
10
11
  @PostMapping
  public ResponseEntity<Void> create(@RequestBody CreateBoardRequestDto createBoardRequestDto) {
      boardService.create(createBoardRequestDto);
      return ResponseEntity.noContent().build();
  }
  
  @GetMapping("/{boardId}")
  public ResponseEntity<BoardResponseDto> getBoard(@PathVariable Long boardId) {
      BoardResponseDto boardResponseDto = boardService.getBoard(boardId);
      return ResponseEntity.ok(boardResponseDto);
  }

3. boardservice

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
  private final BoardRepository boardRepository;
  private final UserClient userClient;
    
  @Transactional
  public void create(CreateBoardRequestDto createBoardRequestDto) {
      Board board = new Board(
              createBoardRequestDto.getTitle(),
              createBoardRequestDto.getContent(),
              createBoardRequestDto.getUserId()
      );

      this.boardRepository.save(board);
  }

  public BoardResponseDto getBoard(Long boardId) {
      // 게시글 불러오기
      Board board = boardRepository.findById(boardId)
              .orElseThrow(() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));

      // user-service로부터 사용자 정보 불러오기
      Optional<UserResponseDto> optionalUserResponseDto = userClient.fetchUser(board.getUserId());
      
      UserDto userDto = null; // 기본값이 Null
      // 유저정보를 가져왔다면 dto에 셋팅
      // 가져오지 않았따면 if작업 X 메모리 보존
      if (optionalUserResponseDto.isPresent()) { 
        UserResponseDto userResponseDto = optionalUserResponseDto.get();
        userDto = new UserDto(
            userResponseDto.getUserId(),
            userResponseDto.getName()
        );
      }
            
      BoardResponseDto boardResponseDto = new BoardResponseDto(
              board.getBoardId(),
              board.getTitle(),
              board.getContent(),
              userDto
      );


      return boardResponseDto;
  }

4. UserClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  private final RestClient restClient;

  // 지금 프로젝트는 RestClient를 사용하여 통신함.
  public UserClient( @Value("${client.user-service.url}") String userServiceUrl ) {
      this.restClient = RestClient.builder()
              .baseUrl(userServiceUrl)
              .build();
  }

  // 최종적으로 사용 가능한 통신 방법으로 특정 정보를 얻어줌
  public Optional<UserResponseDto> fetchUser(Long userId) {
    try {
      UserResponseDto userResponseDto = this.restClient.get()
          .uri("/users/{userId}", userId)
          .retrieve()
          .body(UserResponseDto.class);
      return Optional.ofNullable(userResponseDto);
    } catch (RestClientException e) {
      // 장애가 있을 경우 비어있는 값으로 리턴함       
      return Optional.empty();
    }
  }

5. DTO

  • BoardResponseDto : boardId / title / content / user
  • CreateBoardRequestDto : title / content / userId
  • UserDto : UserId / name
  • UserResponseDto : userId / email / board

6. domain

  • Board : boardId / title / content / userId
This post is licensed under CC BY 4.0 by the author.