개요
- "board-system" 이 우리 서비스의 이름이고, 게시판을 중심으로 기능들이 돌아간다.
- 근데 실질적으로는 게시판의 핵심 가치는 커뮤니케이션이 발생하는 "게시글" 그 자체이며, 게시글이 가장 중요하다.
- 게시글 생성 정책
- 게시글을 생성하기 위해선, 게시글 카테고리를 지정해야한다.
- 게시글 생성시, 작성시점의 nickname 이 박제된다.(변경 불가능. 그 시점의 작성자 닉네임도 보존하는 정책)
- 게시글 카테고리 정책상, 일반 사용자의 게시글 작성이 불가능하다면 게시글을 작성할 수 없다.
- 게시글은 이후 여러가지 부가적인 기능들이 덧붙여질 수 있다. 하지만 이런 기능들의 대상이 되는 게시글이 이들에 대해서는 모르게 설계한다. (맥락 순환참조 방지)
게시글 테이블 설계
CREATE TABLE IF NOT EXISTS articles(
article_id BIGINT NOT NULL PRIMARY KEY,
title VARCHAR(50) NOT NULL,
content VARCHAR(3000) NOT NULL,
board_id BIGINT NOT NULL,
article_category_id BIGINT NOT NULL,
writer_id BIGINT NOT NULL,
writer_nickname VARCHAR(15) NOT NULL,
created_at DATETIME NOT NULL,
modified_at DATETIME NOT NULL
);
- article_id : 게시글 식별자
- title: 제목
- content: 본문
- board_id : 게시판 식별자
- article_category_id: 게시글 카테고리 식별자
- writer_id : 작성자 식별자
- writer_nickname: 작성 시점의 작성자 닉네임
- 현재 nickname 과 구분된다. 작성 시점의 작성자 닉네임을 보존하기 위함이다.
- created_at : 생성시점
- modified_at : 마지막 수정 시점
도메인 엔티티
class Article
internal constructor(
val articleId: Long,
title: String,
content: String,
val boardId: Long,
articleCategoryId: Long,
val writerId: Long,
val writerNickname: String,
val createdAt: AppDateTime,
modifiedAt: AppDateTime,
) {
var title: String = title
private set
var content: String = content
private set
var articleCategoryId: Long = articleCategoryId
private set
var modifiedAt: AppDateTime = modifiedAt
private set
companion object {
fun create(
articleId: Long,
title: String,
content: String,
boardId: Long,
articleCategoryId: Long,
writerId: Long,
writerNickname: String,
createdAt: AppDateTime,
): Article {
return Article(
articleId = articleId,
title = title,
content = content,
boardId = boardId,
articleCategoryId = articleCategoryId,
writerId = writerId,
writerNickname = writerNickname,
createdAt = createdAt,
modifiedAt = createdAt,
)
}
fun restore(
articleId: Long,
title: String,
content: String,
boardId: Long,
articleCategoryId: Long,
writerId: Long,
writerNickname: String,
createdAt: LocalDateTime,
modifiedAt: LocalDateTime,
): Article {
return Article(
articleId = articleId,
title = title,
content = content,
boardId = boardId,
articleCategoryId = articleCategoryId,
writerId = writerId,
writerNickname = writerNickname,
createdAt = AppDateTime.from(createdAt),
modifiedAt = AppDateTime.from(modifiedAt)
)
}
}
fun isWriter(userId: Long): Boolean {
return this.writerId == userId
}
fun update(title: String, content: String, modifiedAt: AppDateTime): UpdateResult {
if (title == this.title && content == this.content) {
return UpdateResult.UNCHANGED
}
this.title = title
this.content = content
this.modifiedAt = modifiedAt
return UpdateResult.CHANGED
}
enum class UpdateResult {
CHANGED, UNCHANGED
}
override fun toString(): String {
return "Article(articleId=$articleId, title='$title', content='$content', boardId=$boardId, articleCategoryId=$articleCategoryId, writerId=$writerId, writerNickname='$writerNickname', createdAt=$createdAt, modifiedAt=$modifiedAt)"
}
}