Skip to content

Driving Adapter 와 UseCase 구현 #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 56 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
2d39bb0
feat: add BoardDtoMapper, DTOs, and dependencies for board functionality
shin-jingyu Jan 5, 2025
c80cd1f
feat(board):add Command UseCase and Controller
silberbullet Jan 6, 2025
5a93160
feat(board) : merge main branch
silberbullet Jan 6, 2025
bca5627
feat. Board Service 구현
wch-os Jan 6, 2025
2419069
Merge remote-tracking branch 'origin/main' into feature/board-service
wch-os Jan 6, 2025
cba79ca
test. Board Service 단위 테스트
wch-os Jan 6, 2025
747209f
feat: Add Lombok dependency and new BoardQuery classes
shin-jingyu Jan 6, 2025
9f8c1be
feat(board) : try for board web module
silberbullet Jan 7, 2025
1d9c470
chore(board):merge main branch
silberbullet Jan 7, 2025
b6ef149
build(board):add Jackson library and configure jsonrootname
silberbullet Jan 7, 2025
ed3750d
feat: Update BoardQueryController, DTOs, and Mapper for board data ha…
shin-jingyu Jan 7, 2025
d2857ab
fix(board): fix dto and controller
silberbullet Jan 7, 2025
46362b7
test(board): add commandController unit test
silberbullet Jan 7, 2025
96756de
Merge branch 'main' into feature/board-service
wch-os Jan 8, 2025
0c4f906
feat: Add BoardQueryControllerTest, update RequestMapping in Controll…
shin-jingyu Jan 8, 2025
81ae62d
refactor: Remove unused code in BoardQueryControllerTest
shin-jingyu Jan 8, 2025
5c07f42
fix: Update boardPage handling in BoardQueryControllerTest.kt
shin-jingyu Jan 9, 2025
bf819d6
feat(board): BoardReadByStatusesUsecase 추가 구현
wch-os Jan 10, 2025
6b14a72
test(board): BoardService 테스트 코드 추가 및 리팩토링
wch-os Jan 10, 2025
e96cabe
refactor: Add beforeSpec initialization to BoardQueryControllerTest
shin-jingyu Jan 11, 2025
2fe1b9c
feat(board): add validation annotation
silberbullet Jan 12, 2025
44ae379
fix(board): fix httpstatus
silberbullet Jan 12, 2025
8d14bc6
fix(board): fix command controller test
silberbullet Jan 12, 2025
85f4519
refactor: refactor BoardQueryControllerTest
shin-jingyu Jan 13, 2025
cc9f2be
refactor: refactor BoardQueryControllerTest
shin-jingyu Jan 13, 2025
2869742
feat: Add BoardReadByStatusesUseCase and related functionality
shin-jingyu Jan 14, 2025
a26fbfe
fix(board): fix command controller and dto
silberbullet Jan 14, 2025
1ff1de5
merge(board): add BoardReadByStatusesUseCase
silberbullet Jan 14, 2025
19f9eeb
chore(board): rename BoardReadByStatusesUsecase
silberbullet Jan 14, 2025
4df2745
chore(board): fix BoardReadByStatusesUsecase and controller
silberbullet Jan 14, 2025
a44c539
test(board): refactor command controller test
silberbullet Jan 14, 2025
9675332
remove(board): BoardReadUseCase-findGeneralBy 삭제
wch-os Jan 15, 2025
6482568
merge(board): merge board-service branch
silberbullet Jan 15, 2025
16be7a4
chore(board): delete findGeneralBy methods
silberbullet Jan 15, 2025
2fe8e09
chore(board): delete findAll methods
shin-jingyu Jan 15, 2025
b36855a
fix(tests): change Bad Request to Not Found in test cases
shin-jingyu Jan 15, 2025
2c70e28
remove(board): BoardQueryPort-findAll 삭제
wch-os Jan 15, 2025
0291bea
chore: remove unused `spring` logging config from application-local.yml
shin-jingyu Jan 19, 2025
93e03e5
refactor: update request mapping to `boards` and remove controller-le…
shin-jingyu Jan 19, 2025
14fad58
refactor(board): refactor command controller
silberbullet Jan 19, 2025
1195926
Merge remote-tracking branch 'origin/main' into feature/driving-adapter
shin-jingyu Jan 20, 2025
7e1f509
fix(board): replaced the `List` parameter with a `Set` in `BoardReadB…
mentalage46 Jan 21, 2025
7e2f07a
fix(board): changed the parameter order of the findByStatuses method …
mentalage46 Jan 21, 2025
665d31b
feat(board): add `ReadModel` return type to `BoardQueryPort`
mentalage46 Jan 21, 2025
7f8057b
merge(board): merge feature config branch
silberbullet Jan 21, 2025
3fbc6df
build(board): add spring-starter-validation
silberbullet Jan 21, 2025
4f1df36
refactor(board): refactor board mapper using @BeanMapping
silberbullet Jan 21, 2025
68ae6d0
feat(board): add soft delete to board
wch-os Jan 22, 2025
bda07b6
Merge remote-tracking branch 'origin/feature/driving-adapter' into fe…
shin-jingyu Jan 22, 2025
1a1d538
feat(board): Add domain models and integrate into driving layer
shin-jingyu Jan 25, 2025
d549d9b
refactor(board): Update BoardReadDetailModel with modifications
shin-jingyu Jan 25, 2025
aeba058
refactor(board): code cleanup and update DTO
shin-jingyu Jan 26, 2025
2919162
refactor: Rename controllers to API and return Page directly
shin-jingyu Jan 26, 2025
9356e69
refactor: Rename controller to API
shin-jingyu Jan 26, 2025
8fda352
merge(board): merge board-service branch
silberbullet Jan 29, 2025
5ade3f8
fix(board): fix command url and dto and test code
silberbullet Jan 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ repositories {
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-validation")

// logging
implementation("org.springframework.boot:spring-boot-starter-log4j2")
Expand Down Expand Up @@ -76,6 +77,8 @@ dependencies {
testImplementation("io.kotest:kotest-runner-junit5:5.9.1")
testImplementation("io.mockk:mockk:1.13.12")
testImplementation(kotlin("script-runtime"))
testCompileOnly("org.projectlombok:lombok") // 테스트 의존성 추가
testAnnotationProcessor("org.projectlombok:lombok") // 테스트 의존성 추가
testImplementation("io.kotest.extensions:kotest-extensions-spring:1.1.3")
}

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

tasks.withType<JavaCompile> {
options.compilerArgs.addAll(listOf(
"--enable-preview"
//"-Amapstruct.defaultComponentModel=spring",
"--enable-preview",
"-Amapstruct.defaultComponentModel=spring",
))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package me.nettee.board.adapter.driving.web;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardCommandResponse;
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardCreateCommand;
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardUpdateCommand;
import me.nettee.board.adapter.driving.web.mapper.BoardDtoMapper;
import me.nettee.board.application.usecase.BoardCreateUseCase;
import me.nettee.board.application.usecase.BoardDeleteUseCase;
import me.nettee.board.application.usecase.BoardUpdateUseCase;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 와일드카드 임포트(*, asterisk) 사용을 삼가는 것이 좋습니다.

구글 Java 코드 스타일 가이드에서 제안하는 내용입니다.

다음은 최대한 사용을 삼가는 것이 좋습니다.

  • 와일드카드 임포트(import ~.*)
  • 정적 와일드카드 임포트(static import ~.*)
  • 정적 임포트(static import ~)

단, 정적 임포트(static import ~)는 이 파일 내에서 자주 사용되거나, 기타 이유로 팀이 적절하다고 생각 시 팀 내에서 사용을 허용할 수 있습니다. (유틸리티 함수 등)


@RestController
@RequestMapping("/api/v1/board")
@RequiredArgsConstructor
public class BoardCommandController {

private final BoardCreateUseCase boardCreateUseCase;
private final BoardUpdateUseCase boardUpdateUseCase;
private final BoardDeleteUseCase boardDeleteUseCase;
private final BoardDtoMapper boardDtoMapper;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💊 기호의 차이이지만, mapper로 명명하는 것을 제안드립니다.

boardDtoMapper는 그 역할이 매우 단순하며, 이 클래스 내에서 가장 기본이 되는 매퍼입니다.

따라서 복잡한 기능을 수행하는 다른 빈들에 비해 단순한 네이밍으로도 충분히 식별할 수 있습니다.
전체적으로 코드를 읽을 때도 중요한 작업을 수행하기보다 보조적으로 사용되므로,
mapper라는 이름만으로 충분히 그 역할을 잘 표현할 수 있다고 생각합니다.

하지만 취향의 차이이기 때문에, 생각하시는 것과 다르다면 어떤 부분에서 다르게 생각하시는지 등 편하게 말씀 부탁드립니다!


@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public BoardCommandResponse createBoard(@RequestBody @Valid BoardCreateCommand boardCreateCommand) {
// Map to Domain
var board = boardDtoMapper.toDomain(boardCreateCommand);

return BoardCommandResponse.builder()
.board(boardCreateUseCase.createBoard(board))
.build();
}

@PatchMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public BoardCommandResponse updateBoard(@PathVariable("id") Long id,
@Valid @RequestBody BoardUpdateCommand boardUpdateCommand) {
// Map to Domain
var board = boardDtoMapper.toDomain(id, boardUpdateCommand);

return BoardCommandResponse.builder()
.board(boardUpdateUseCase.updateBoard(board))
.build();
}

@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteBoard(@PathVariable("id") Long id) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💊 Path Variables에서 기본 자료형을 사용해 NOT NULL을 표현할 수 있습니다.

래퍼 클래스의 사용은 필요할 때만 사용하는 것이 고전적으로 권장되어 왔습니다.
추가적인 객체 생성으로 힙 영역에 데이터가 추가로 할당되는 등 불필요한 오버헤드를 늘리기 때문입니다.

단, 레코드나 일부 클래스 설계 시, 추후 변경 가능성을 고려하여 래퍼 클래스 Long 및 별도 not null 체크 로직을 사용해 관리할 수 있습니다. 현황으로는 NOT NULL이지만 추후 Nullable로 바뀔 때, 원시 타입을 참조 타입으로 매번 수정하는 것이 불편하기 때문입니다.

이때는 파라미터로 바로 받고 있기 때문에, 원시 타입 long을 사용할 수도 있습니다.
또는 래퍼 클래스가 상수 풀로 관리되므로 동일 아이디에 대해서는 추가 객체 할당이 자주 되지는 않을 수도 있어 감수할 만한 오버헤드로 생각할 수도 있습니다.

개인적으로 이 계층만 생각했을 때는 long 타입이 적절하다고 생각하고 있습니다. 만약 서비스 등에서 어차피 Long으로 변환되는 구간이 생길 것으로 예측하더라도, '필요할 때 객체를 생성하는 방식'이 좋아 보입니다.

boardDeleteUseCase.deleteBoard(id);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💊 클래스 이름을 BoardQueryApi로 제안드립니다.

자세한 이유는 다른 리뷰 항목에 포함하였습니다!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@merge-simpson 제안 감사합니다 👍
BoardQueryController -> BoardQueryApi 클래스 이름 변경 완료되었습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package me.nettee.board.adapter.driving.web;

import lombok.RequiredArgsConstructor;
import me.nettee.board.adapter.driving.web.dto.BoardQueryDto.BoardDetailResponse;
import me.nettee.board.adapter.driving.web.dto.BoardQueryDto.BoardSummaryResponse;
import me.nettee.board.adapter.driving.web.mapper.BoardDtoMapper;
import me.nettee.board.application.domain.type.BoardStatus;
import me.nettee.board.application.usecase.BoardReadByStatusesUseCase;
import me.nettee.board.application.usecase.BoardReadUseCase;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 와일드카드 임포트를 삼가는 것이 좋습니다.

자세한 이유는 다른 리뷰 항목에 포함하였습니다!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@merge-simpson 좋은 의견 감사합니다 👍
놓쳤던 부분을 확인해주셔서 감사합니다.

import java.util.Set;

@RestController
@RequestMapping("/api/v1/boards")
@RequiredArgsConstructor
public class BoardQueryController {
private final BoardReadUseCase boardReadUseCase;
private final BoardDtoMapper boardDtoMapper;
private final BoardReadByStatusesUseCase boardReadByStatusesUseCase;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 필드 선언 순서에서 mapper가 usecase들 사이로 침투해 있습니다.

도메인중심적인 배치와 계층적인 배치에 대해 다음 전략을 선호하고 있습니다.

  • 패키지를 분류할 때는 도메인중심적으로 합니다.
  • 한 클래스 내에서 각종 선언을 분류할 때는 계층적인 분류를 선호합니다.

따라서 다음과 같은 배치를 제안드립니다.

private final BoardReadUseCase boardReadUseCase;
private final BoardReadByStatusesUseCase boardReadByStatusesUseCase;

private final BoardDtoMapper boardDtoMapper;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@merge-simpson 좋은 의견 감사합니다 👍
도메인 중심 배치와 계층적 배치에 대해 미처 생각하지 못했는데 좋은 의견 감사합니다.


@GetMapping("/{boardId}")
public BoardDetailResponse getBoard(@PathVariable("boardId") long boardId) {
var board = boardReadUseCase.getBoard(boardId);

return boardDtoMapper.toDtoDetail(board);
}

@GetMapping
public Page<BoardSummaryResponse> getBoardsByStatuses(@RequestParam Set<BoardStatus> statuses, Pageable pageable) {
var board = boardReadByStatusesUseCase.findByStatuses(statuses, pageable);

return board.map(boardDtoMapper::toDtoSummary);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package me.nettee.board.adapter.driving.web.dto;

import com.fasterxml.jackson.annotation.JsonRootName;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Builder;
import me.nettee.board.application.domain.Board;

public final class BoardCommandDto {

private BoardCommandDto() {
}

public record BoardCreateCommand(
@NotBlank(message = "제목을 입력하십시오.")
@Size(min = 3, message = "제목은 세 글자 이상 입력하세요.")
String title,
@NotBlank(message = "본문을 입력하십시오")
@Size(min = 3, message = "제목은 세 글자 이상 입력하세요.")
String content
) {
}

public record BoardUpdateCommand(
@NotBlank(message = "제목을 입력하십시오.")
@Size(min = 3, message = "제목은 세 글자 이상 입력하세요.")
String title,
@NotBlank(message = "본문을 입력하십시오")
@Size(min = 3, message = "제목은 세 글자 이상 입력하세요.")
String content
) {
}
Comment on lines +14 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💊 테스트 편의성을 위해 요청 DTO에도 @Builder 애노테이션을 추가할 수 있습니다.

테스트 시 편리한 객체 생성을 위해 요청 DTO에도 빌더 애노테이션을 추가할 수 있습니다.

💊 (다음 실습) 반복되는 정적 상수 문자열은 도메인 계층의 정책에서 상수 관리로 제공할 수 있습니다.

도메인 및 애플리케이션 계층에서 관리가 선행되어야 하므로 이번 리뷰에서는 간소화하여 작성드리는 것이 좋아 보입니다. 또, 이번 실습 단계보다는 다음 실습(멀티모듈 프로젝트)에서 상세하게 맞춰 보는 것이 더 적절할 수도 있다고 생각합니다.


@Builder
@JsonRootName("board")
public record BoardCommandResponse(
Board board
) {
}
Comment on lines +34 to +39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💊 이 조건에서 @JsonRootName(value = "board") 애노테이션과 최외곽 객체의 명명 중 선택하는 것이 좋아 보입니다.

최외곽으로 반환하는 것이 단일 객체일 때, 테스트 결과 @JsonRootName(...) 애노테이션이 동작하지 않는 것으로 확인했습니다. (이 브랜치의 공동 작업자인 @shin-jingyu 님과 확인하였습니다.)

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗️ 애플리케이션·도메인 계층의 작업자들과 논의하여 ReadModel 등의 설계가 필요해 보입니다.

BoardSummaryResponse도 필요하다면 생성할 수 있습니다.
하지만 원론적으로 BoardSummaryReadModel의 설계가 애플리케이션 계층에 필요해 보였고, 그것을 초기에 그대로 사용할 수 있어 보입니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package me.nettee.board.adapter.driving.web.dto;

import com.fasterxml.jackson.annotation.JsonRootName;
import lombok.Builder;
import me.nettee.board.application.domain.type.BoardStatus;

import java.time.Instant;

public final class BoardQueryDto {
private BoardQueryDto() {}


@Builder
public record BoardSummaryResponse(
Long id,
String title,
BoardStatus status,
Instant createdAt
){}


@Builder
@JsonRootName("board")
public record BoardDetailResponse(
Long id,
String title,
String content,
BoardStatus status,
Instant createdAt,
Instant updatedAt
){}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💊 유지보수성을 위해 BoardDetail board 반환을 제안드립니다.

독립적인 BoardDetailResponse 객체를 이처럼 평탄화하여 작성하도록 설계하는 것도 좋습니다.

하지만 다음과 같은 단점을 느꼈습니다.

  • 도메인 필드 목록이 바뀌면 이러한 유형의 모든 DTO를 수정하여야 합니다. (도메인 필드와 중복되는 코드이기도 합니다.)
  • 추후 추가되는 외적 정보가 있을 때, board 필드와 구분하여 추가하려면 계층화하는 공사가 필요합니다.

이번에 애플리케이션 계층 및 도메인 계층에서 조회 모델(Read model)을 관리하도록 수정되었기 때문에, 반환 시에도 조회 모델을 사용하도록 개선할 수 있을 것 같습니다.

@JsonRootName(value = "그루핑할 명칭") 애노테이션을 알게 된 것은 유익한 공부였습니다. 👍

💬 별개로 참조 타입 반환 시, 각 필드에 @JsonInclude(Include.NON_NULL) 애노테이션을 고려할 수 있습니다.

애플리케이션·도메인 계층과 독립적인 데이터로 관리할 때 붙일 수 있는 애노테이션입니다.

클라이언트에 반환 시 null 대신 undefined로 도착하도록 제어하는 옵션으로 생각할 수 있습니다.
Jackson은 해당 필드가 null이면 실제 JSON 문자열에 포함하지 않습니다.
(Gson 사용 시 해당 애노테이션이 없어도 자동으로 null 필드를 반환하지 않고 무시합니다.)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package me.nettee.board.adapter.driving.web.mapper;

import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardCreateCommand;
import me.nettee.board.adapter.driving.web.dto.BoardCommandDto.BoardUpdateCommand;
import me.nettee.board.adapter.driving.web.dto.BoardQueryDto.BoardDetailResponse;
import me.nettee.board.adapter.driving.web.dto.BoardQueryDto.BoardSummaryResponse;
import me.nettee.board.application.domain.Board;
import me.nettee.board.application.model.BoardReadDetailModel;
import me.nettee.board.application.model.BoardReadSummaryModel;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring")
public interface BoardDtoMapper {

@BeanMapping(ignoreByDefault = true)
@Mapping(target = "title", source = "title")
@Mapping(target = "content", source = "content")
Board toDomain(BoardCreateCommand command);

@BeanMapping(ignoreByDefault = true)
@Mapping(target = "id", source = "id")
@Mapping(target = "title", source = "command.title")
@Mapping(target = "content", source = "command.content")
Board toDomain(Long id, BoardUpdateCommand command);

BoardDetailResponse toDtoDetail(BoardReadDetailModel board);

BoardSummaryResponse toDtoSummary(BoardReadSummaryModel board);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package me.nettee.board.application.model;

import me.nettee.board.application.domain.type.BoardStatus;

import java.time.Instant;

public record BoardReadDetailModel(
Long id,
String title,
String content,
BoardStatus status,
Instant createdAt,
Instant updatedAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package me.nettee.board.application.model;

import me.nettee.board.application.domain.type.BoardStatus;

import java.time.Instant;

public record BoardReadSummaryModel(
Long id,
String title,
String content,
BoardStatus status,
Instant createdAt,
Instant updatedAt
) {
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package me.nettee.board.application.port;

import java.util.List;
import me.nettee.board.application.domain.Board;
import me.nettee.board.application.domain.type.BoardStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.Optional;

public interface BoardQueryPort {

Page<Board> findAll(Pageable pageable);

Optional<Board> findById(Long id);

Page<Board> findByStatusesList(Pageable pageable, List<BoardStatus> statuses);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package me.nettee.board.application.service;

import lombok.RequiredArgsConstructor;
import me.nettee.board.application.domain.Board;
import me.nettee.board.application.port.BoardCommandPort;
import me.nettee.board.application.usecase.BoardCreateUseCase;
import me.nettee.board.application.usecase.BoardDeleteUseCase;
import me.nettee.board.application.usecase.BoardUpdateUseCase;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class BoardCommandService implements BoardCreateUseCase, BoardUpdateUseCase, BoardDeleteUseCase {

private final BoardCommandPort boardCommandPort;

public Board createBoard(Board board) {
return boardCommandPort.create(board);
}

public Board updateBoard(Board board) {
return boardCommandPort.update(board);
}

public void deleteBoard(Long id) {
boardCommandPort.delete(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package me.nettee.board.application.service;

import java.util.List;
import lombok.RequiredArgsConstructor;
import me.nettee.board.application.domain.Board;
import me.nettee.board.application.domain.type.BoardStatus;
import me.nettee.board.application.port.BoardQueryPort;
import me.nettee.board.application.usecase.BoardReadByStatusesUseCase;
import me.nettee.board.application.usecase.BoardReadUseCase;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class BoardQueryService implements BoardReadUseCase, BoardReadByStatusesUseCase {

private final BoardQueryPort boardQueryPort;

@Override
public Board getBoard(Long id) {
return boardQueryPort.findById(id).orElseThrow(
() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));
}

@Override
public Page<Board> findByStatuses(Pageable pageable, List<BoardStatus> statuses) {
return boardQueryPort.findByStatusesList(pageable, statuses);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package me.nettee.board.application.usecase;

import me.nettee.board.application.domain.type.BoardStatus;
import me.nettee.board.application.model.BoardReadSummaryModel;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.Set;

public interface BoardReadByStatusesUseCase {

Page<BoardReadSummaryModel> findByStatuses(Set<BoardStatus> statuses, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package me.nettee.board.application.usecase;

import me.nettee.board.application.domain.Board;
import me.nettee.board.application.model.BoardReadDetailModel;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface BoardReadUseCase {

Board getBoard(Long id);

Page<Board> findGeneralBy(Pageable pageable);
BoardReadDetailModel getBoard(Long id);
}
7 changes: 6 additions & 1 deletion src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ spring:
username: ${DEMO_POSTGRESQL_USERNAME:root}
password: ${DEMO_POSTGRESQL_PASSWORD:root}

jackson:
serialization:
wrap-root-value: true

logging:
level:
root: debug
root: debug

Loading