Skip to content

Commit 7ce0835

Browse files
authored
Merge pull request #24 from nettee-space/feature/driving-adapter
Driving Adapter 와 UseCase 구현
2 parents 0d26152 + 5ade3f8 commit 7ce0835

22 files changed

+783
-15
lines changed

build.gradle.kts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ repositories {
3636
dependencies {
3737
implementation("org.springframework.boot:spring-boot-starter-web")
3838
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
39+
implementation("org.springframework.boot:spring-boot-starter-validation")
3940

4041
// logging
4142
implementation("org.springframework.boot:spring-boot-starter-log4j2")
@@ -76,6 +77,8 @@ dependencies {
7677
testImplementation("io.kotest:kotest-runner-junit5:5.9.1")
7778
testImplementation("io.mockk:mockk:1.13.12")
7879
testImplementation(kotlin("script-runtime"))
80+
testCompileOnly("org.projectlombok:lombok") // 테스트 의존성 추가
81+
testAnnotationProcessor("org.projectlombok:lombok") // 테스트 의존성 추가
7982
testImplementation("io.kotest.extensions:kotest-extensions-spring:1.1.3")
8083
}
8184

@@ -100,8 +103,8 @@ tasks.withType<KotlinCompile> {
100103

101104
tasks.withType<JavaCompile> {
102105
options.compilerArgs.addAll(listOf(
103-
"--enable-preview"
104-
//"-Amapstruct.defaultComponentModel=spring",
106+
"--enable-preview",
107+
"-Amapstruct.defaultComponentModel=spring",
105108
))
106109
}
107110

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package me.nettee.board.adapter.driving.web;
2+
3+
import jakarta.validation.Valid;
4+
import lombok.RequiredArgsConstructor;
5+
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardCommandResponse;
6+
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardCreateCommand;
7+
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardUpdateCommand;
8+
import me.nettee.board.adapter.driving.web.mapper.BoardDtoMapper;
9+
import me.nettee.board.application.usecase.BoardCreateUseCase;
10+
import me.nettee.board.application.usecase.BoardDeleteUseCase;
11+
import me.nettee.board.application.usecase.BoardUpdateUseCase;
12+
import org.springframework.http.HttpStatus;
13+
import org.springframework.web.bind.annotation.*;
14+
15+
@RestController
16+
@RequestMapping("/api/v1/boards")
17+
@RequiredArgsConstructor
18+
public class BoardCommandApi {
19+
20+
private final BoardCreateUseCase boardCreateUseCase;
21+
private final BoardUpdateUseCase boardUpdateUseCase;
22+
private final BoardDeleteUseCase boardDeleteUseCase;
23+
private final BoardDtoMapper boardDtoMapper;
24+
25+
@PostMapping
26+
@ResponseStatus(HttpStatus.CREATED)
27+
public BoardCommandResponse createBoard(@RequestBody @Valid BoardCreateCommand boardCreateCommand) {
28+
// Map to Domain
29+
var board = boardDtoMapper.toDomain(boardCreateCommand);
30+
31+
return BoardCommandResponse.builder()
32+
.board(boardCreateUseCase.createBoard(board))
33+
.build();
34+
}
35+
36+
@PatchMapping("/{id}")
37+
@ResponseStatus(HttpStatus.OK)
38+
public BoardCommandResponse updateBoard(@PathVariable("id") Long id,
39+
@Valid @RequestBody BoardUpdateCommand boardUpdateCommand) {
40+
// Map to Domain
41+
var board = boardDtoMapper.toDomain(id, boardUpdateCommand);
42+
43+
return BoardCommandResponse.builder()
44+
.board(boardUpdateUseCase.updateBoard(board))
45+
.build();
46+
}
47+
48+
@DeleteMapping("/{id}")
49+
@ResponseStatus(HttpStatus.NO_CONTENT)
50+
public void deleteBoard(@PathVariable("id") Long id) {
51+
boardDeleteUseCase.deleteBoard(id);
52+
}
53+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package me.nettee.board.adapter.driving.web;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import me.nettee.board.adapter.driving.web.dto.BoardQueryDto.BoardDetailResponse;
5+
import me.nettee.board.adapter.driving.web.mapper.BoardDtoMapper;
6+
import me.nettee.board.application.domain.type.BoardStatus;
7+
import me.nettee.board.application.model.BoardReadSummaryModel;
8+
import me.nettee.board.application.usecase.BoardReadByStatusesUseCase;
9+
import me.nettee.board.application.usecase.BoardReadUseCase;
10+
import org.springframework.data.domain.Page;
11+
import org.springframework.data.domain.Pageable;
12+
import org.springframework.web.bind.annotation.GetMapping;
13+
import org.springframework.web.bind.annotation.PathVariable;
14+
import org.springframework.web.bind.annotation.RequestMapping;
15+
import org.springframework.web.bind.annotation.RequestParam;
16+
import org.springframework.web.bind.annotation.RestController;
17+
import java.util.Set;
18+
19+
@RestController
20+
@RequestMapping("/api/v1/boards")
21+
@RequiredArgsConstructor
22+
public class BoardQueryApi {
23+
private final BoardReadUseCase boardReadUseCase;
24+
private final BoardReadByStatusesUseCase boardReadByStatusesUseCase;
25+
26+
private final BoardDtoMapper boardDtoMapper;
27+
28+
@GetMapping("/{boardId}")
29+
public BoardDetailResponse getBoard(@PathVariable("boardId") long boardId) {
30+
var board = boardReadUseCase.getBoard(boardId);
31+
32+
return boardDtoMapper.toDtoDetail(board);
33+
}
34+
35+
@GetMapping
36+
public Page<BoardReadSummaryModel> getBoardsByStatuses(@RequestParam Set<BoardStatus> statuses, Pageable pageable) {
37+
return boardReadByStatusesUseCase.findByStatuses(statuses, pageable);
38+
}
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package me.nettee.board.adapter.driving.web.dto;
2+
3+
import com.fasterxml.jackson.annotation.JsonRootName;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.Size;
6+
import lombok.Builder;
7+
import me.nettee.board.application.domain.Board;
8+
9+
public final class BoardCommandDto {
10+
11+
private BoardCommandDto() {
12+
}
13+
14+
public record BoardCreateCommand(
15+
@NotBlank(message = "제목을 입력하십시오.")
16+
@Size(min = 3, message = "제목은 세 글자 이상 입력하세요.")
17+
String title,
18+
@NotBlank(message = "본문을 입력하십시오")
19+
@Size(min = 3, message = "제목은 세 글자 이상 입력하세요.")
20+
String content
21+
) {
22+
}
23+
24+
public record BoardUpdateCommand(
25+
@NotBlank(message = "제목을 입력하십시오.")
26+
@Size(min = 3, message = "제목은 세 글자 이상 입력하세요.")
27+
String title,
28+
@NotBlank(message = "본문을 입력하십시오")
29+
@Size(min = 3, message = "제목은 세 글자 이상 입력하세요.")
30+
String content
31+
) {
32+
}
33+
34+
@Builder
35+
@JsonRootName("board")
36+
public record BoardCommandResponse(
37+
Board board
38+
) {
39+
}
40+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package me.nettee.board.adapter.driving.web.dto;
2+
3+
import com.fasterxml.jackson.annotation.JsonRootName;
4+
import lombok.Builder;
5+
import me.nettee.board.application.domain.type.BoardStatus;
6+
import me.nettee.board.application.model.BoardReadDetailModel;
7+
import me.nettee.board.application.model.BoardReadSummaryModel;
8+
9+
import java.time.Instant;
10+
11+
public final class BoardQueryDto {
12+
private BoardQueryDto() {}
13+
14+
@Builder
15+
public record BoardDetailResponse(
16+
BoardReadDetailModel board
17+
){}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package me.nettee.board.adapter.driving.web.mapper;
2+
3+
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardCreateCommand;
4+
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardUpdateCommand;
5+
import me.nettee.board.adapter.driving.web.dto.BoardQueryDto.BoardDetailResponse;
6+
import me.nettee.board.application.domain.Board;
7+
import me.nettee.board.application.model.BoardReadDetailModel;
8+
import org.mapstruct.Mapper;
9+
10+
@Mapper(componentModel = "spring")
11+
public interface BoardDtoMapper {
12+
13+
Board toDomain(BoardCreateCommand command);
14+
15+
Board toDomain(Long id, BoardUpdateCommand command);
16+
17+
BoardDetailResponse toDtoDetail(BoardReadDetailModel board);
18+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package me.nettee.board.application.model;
2+
3+
import java.time.Instant;
4+
import me.nettee.board.application.domain.type.BoardStatus;
5+
6+
public record BoardReadDetailModel(
7+
Long id,
8+
String title,
9+
String content,
10+
BoardStatus status,
11+
Instant createdAt,
12+
Instant updatedAt,
13+
Instant deletedAt
14+
) {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package me.nettee.board.application.model;
2+
3+
import java.time.Instant;
4+
5+
public record BoardReadSummaryModel(
6+
Long id,
7+
String title,
8+
String content,
9+
Instant createdAt,
10+
Instant updatedAt
11+
){}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package me.nettee.board.application.port;
22

3+
import java.util.Optional;
34
import me.nettee.board.application.domain.Board;
45

56
public interface BoardCommandPort {
67

8+
Optional<Board> findById(Long id);
9+
710
Board create(Board board);
811

912
Board update(Board board);
1013

11-
void delete(Long id);
12-
14+
void delete(Board id);
1315
}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package me.nettee.board.application.port;
22

3-
import me.nettee.board.application.domain.Board;
3+
import java.util.Optional;
4+
import java.util.Set;
5+
import me.nettee.board.application.domain.type.BoardStatus;
6+
import me.nettee.board.application.model.BoardReadDetailModel;
7+
import me.nettee.board.application.model.BoardReadSummaryModel;
48
import org.springframework.data.domain.Page;
59
import org.springframework.data.domain.Pageable;
610

7-
import java.util.Optional;
8-
911
public interface BoardQueryPort {
1012

11-
Page<Board> findAll(Pageable pageable);
13+
Optional<BoardReadDetailModel> findById(Long id);
1214

13-
Optional<Board> findById(Long id);
15+
Page<BoardReadSummaryModel> findByStatusesList(Pageable pageable, Set<BoardStatus> statuses);
1416

1517
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package me.nettee.board.application.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import me.nettee.board.application.domain.Board;
5+
import me.nettee.board.application.model.BoardReadDetailModel;
6+
import me.nettee.board.application.port.BoardCommandPort;
7+
import me.nettee.board.application.port.BoardQueryPort;
8+
import me.nettee.board.application.usecase.BoardCreateUseCase;
9+
import me.nettee.board.application.usecase.BoardDeleteUseCase;
10+
import me.nettee.board.application.usecase.BoardUpdateUseCase;
11+
import org.springframework.stereotype.Service;
12+
13+
@Service
14+
@RequiredArgsConstructor
15+
public class BoardCommandService implements BoardCreateUseCase, BoardUpdateUseCase, BoardDeleteUseCase {
16+
17+
private final BoardCommandPort boardCommandPort;
18+
19+
public Board createBoard(Board board) {
20+
return boardCommandPort.create(board);
21+
}
22+
23+
public Board updateBoard(Board board) {
24+
return boardCommandPort.update(board);
25+
}
26+
27+
public void deleteBoard(Long id) {
28+
Board board = boardCommandPort.findById(id).orElseThrow(
29+
() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));
30+
31+
board.softDelete();
32+
33+
boardCommandPort.delete(board);
34+
}
35+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package me.nettee.board.application.service;
2+
3+
import java.util.Set;
4+
import lombok.RequiredArgsConstructor;
5+
import me.nettee.board.application.domain.type.BoardStatus;
6+
import me.nettee.board.application.model.BoardReadDetailModel;
7+
import me.nettee.board.application.model.BoardReadSummaryModel;
8+
import me.nettee.board.application.port.BoardQueryPort;
9+
import me.nettee.board.application.usecase.BoardReadByStatusesUseCase;
10+
import me.nettee.board.application.usecase.BoardReadUseCase;
11+
import org.springframework.data.domain.Page;
12+
import org.springframework.data.domain.Pageable;
13+
import org.springframework.stereotype.Service;
14+
15+
@Service
16+
@RequiredArgsConstructor
17+
public class BoardQueryService implements BoardReadUseCase, BoardReadByStatusesUseCase {
18+
19+
private final BoardQueryPort boardQueryPort;
20+
21+
@Override
22+
public BoardReadDetailModel getBoard(Long id) {
23+
return boardQueryPort.findById(id).orElseThrow(
24+
() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));
25+
}
26+
27+
@Override
28+
public Page<BoardReadSummaryModel> findByStatuses(Set<BoardStatus> statuses, Pageable pageable) {
29+
return boardQueryPort.findByStatusesList(pageable, statuses);
30+
}
31+
}

src/main/java/me/nettee/board/application/usecase/BoardCreateUseCase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
public interface BoardCreateUseCase {
66

77
Board createBoard(Board board);
8+
89
}

src/main/java/me/nettee/board/application/usecase/BoardDeleteUseCase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
public interface BoardDeleteUseCase {
44

55
void deleteBoard(Long id);
6+
67
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package me.nettee.board.application.usecase;
2+
3+
import java.util.Set;
4+
import me.nettee.board.application.domain.type.BoardStatus;
5+
import me.nettee.board.application.model.BoardReadSummaryModel;
6+
import org.springframework.data.domain.Page;
7+
import org.springframework.data.domain.Pageable;
8+
9+
public interface BoardReadByStatusesUseCase {
10+
11+
Page<BoardReadSummaryModel> findByStatuses(Set<BoardStatus> statuses, Pageable pageable);
12+
13+
}
14+
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package me.nettee.board.application.usecase;
22

3-
import me.nettee.board.application.domain.Board;
4-
import org.springframework.data.domain.Page;
5-
import org.springframework.data.domain.Pageable;
3+
import me.nettee.board.application.model.BoardReadDetailModel;
64

75
public interface BoardReadUseCase {
86

9-
Board getBoard(Long id);
7+
BoardReadDetailModel getBoard(Long id);
108

11-
Page<Board> findGeneralBy(Pageable pageable);
129
}

src/main/java/me/nettee/board/application/usecase/BoardUpdateUseCase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
public interface BoardUpdateUseCase {
55

66
Board updateBoard(Board board);
7+
78
}

0 commit comments

Comments
 (0)