🏗️ 프로젝트 구조 web, core, infrastructure 모듈로 분리

This commit is contained in:
jini
2022-11-30 02:59:47 +09:00
parent c72f7be010
commit b2561d6608
29 changed files with 309 additions and 111 deletions

View File

@@ -0,0 +1,8 @@
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")
compileOnly("org.springframework.boot:spring-boot-configuration-processor")
implementation("org.springdoc:springdoc-openapi-ui:1.6.13")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
}

View File

@@ -0,0 +1,19 @@
package me.jiniworld.demohx.application.notice.domain
import me.jiniworld.demohx.application.notice.port.output.NoticeDetail
import me.jiniworld.demohx.application.notice.port.output.NoticeSimple
import me.jiniworld.demohx.application.util.DateTimeUtils
import java.time.LocalDateTime
data class Notice(
val id: Long,
val title: String,
val content: String,
val createdAt: LocalDateTime,
) {
fun mapToNoticeSimple() =
NoticeSimple(id = id, title = title, createdOn = DateTimeUtils.toDateString(createdAt))
fun mapToNoticeDetail() =
NoticeDetail(id = id, title = title, content = content, createdAt = DateTimeUtils.toString(createdAt))
}

View File

@@ -0,0 +1,10 @@
package me.jiniworld.demohx.application.notice.port.input
import me.jiniworld.demohx.application.notice.port.output.NoticeDetail
import me.jiniworld.demohx.application.notice.port.output.NoticeSimple
import org.springframework.data.domain.Pageable
interface GetNoticeQuery {
fun getNoticeSimple(pageable: Pageable): List<NoticeSimple>?
fun getNoticeDetail(noticeId: Long): NoticeDetail?
}

View File

@@ -0,0 +1,9 @@
package me.jiniworld.demohx.application.notice.port.output
import me.jiniworld.demohx.application.notice.domain.Notice
import org.springframework.data.domain.Pageable
interface LoadNoticePort {
fun loadNotices(pageable: Pageable): List<Notice>?
fun loadNotice(id: Long): Notice?
}

View File

@@ -0,0 +1,8 @@
package me.jiniworld.demohx.application.notice.port.output
data class NoticeDetail(
val id: Long,
val title: String,
val content: String,
val createdAt: String,
)

View File

@@ -0,0 +1,7 @@
package me.jiniworld.demohx.application.notice.port.output
data class NoticeSimple(
val id: Long,
val title: String,
val createdOn: String,
)

View File

@@ -0,0 +1,24 @@
package me.jiniworld.demohx.application.notice.service
import me.jiniworld.demohx.application.notice.port.input.GetNoticeQuery
import me.jiniworld.demohx.application.notice.port.output.LoadNoticePort
import me.jiniworld.demohx.application.notice.domain.Notice
import me.jiniworld.demohx.application.notice.port.output.NoticeDetail
import me.jiniworld.demohx.application.notice.port.output.NoticeSimple
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime
@Transactional(readOnly = true)
@Component
internal class GetNoticeService(
private val loadNoticePort: LoadNoticePort,
) : GetNoticeQuery {
override fun getNoticeSimple(pageable: Pageable) =
loadNoticePort.loadNotices(pageable)?.map { it.mapToNoticeSimple() }
override fun getNoticeDetail(id: Long): NoticeDetail? =
loadNoticePort.loadNotice(id)?.mapToNoticeDetail()
}

View File

@@ -0,0 +1,62 @@
package me.jiniworld.demohx.application.util
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoField
import java.time.temporal.TemporalAdjusters
object DateTimeUtils {
private const val SIMPLE_PATTERN_MONTH = "yyyyMM"
private const val SIMPLE_PATTERN_DATE = "yyyyMMdd"
private const val DEFAULT_PATTERN_DATE = "yyyy-MM-dd"
private const val DEFAULT_PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss"
private const val DEFAULT_PATTERN_TIME = "HH:mm:ss"
private val LOCALTIME_START = LocalTime.of(0, 0)
private val LOCALTIME_END = LocalTime.of(23, 59, 59)
val FORMATTER_MONTH_SIMPLE: DateTimeFormatter = DateTimeFormatter.ofPattern(SIMPLE_PATTERN_MONTH)
val FORMATTER_DATE_SIMPLE: DateTimeFormatter = DateTimeFormatter.ofPattern(SIMPLE_PATTERN_DATE)
val FORMATTER_DATE: DateTimeFormatter = DateTimeFormatter.ofPattern(DEFAULT_PATTERN_DATE)
val FORMATTER_DATETIME: DateTimeFormatter = DateTimeFormatter.ofPattern(DEFAULT_PATTERN_DATETIME)
val FORMATTER_TIME: DateTimeFormatter = DateTimeFormatter.ofPattern(DEFAULT_PATTERN_TIME)
fun getFirstDate(month: String): LocalDate =
LocalDate.of(Integer.parseInt(month.substring(0, 4)), Integer.parseInt(month.substring(4, 6)), 1)
fun getLastDate(month: String): LocalDate =
getFirstDate(month).with(TemporalAdjusters.lastDayOfMonth())
fun getFirstDateTime(date: LocalDate): LocalDateTime =
LocalDateTime.of(date, LOCALTIME_START)
fun getLastDateTime(date: LocalDate): LocalDateTime =
LocalDateTime.of(date.with(TemporalAdjusters.lastDayOfMonth()), LOCALTIME_END)
fun getFirstDateTime(date: LocalDate, type: AnalysisType): LocalDateTime =
when (type) {
AnalysisType.weekly -> LocalDateTime.of(date.minusDays((date[ChronoField.DAY_OF_WEEK] - 1).toLong()).minusWeeks(6), LOCALTIME_START)
AnalysisType.monthly -> date.minusDays((date[ChronoField.DAY_OF_MONTH] - 1).toLong()).minusMonths(6).atStartOfDay()
else -> LocalDateTime.of(date.minusDays((date[ChronoField.DAY_OF_WEEK] - 1).toLong()), LOCALTIME_START)
}
fun getLastDateTime(date: LocalDate, type: AnalysisType): LocalDateTime =
when (type) {
AnalysisType.monthly -> LocalDateTime.of(date.minusDays(date[ChronoField.DAY_OF_MONTH].toLong()).plusMonths(1), LOCALTIME_END)
else -> LocalDateTime.of(date.plusDays((7 - date[ChronoField.DAY_OF_WEEK]).toLong()), LOCALTIME_END)
}
fun toString(date: LocalDate) = date.format(FORMATTER_DATE)
fun toString(dateTime: LocalDateTime) = dateTime.format(FORMATTER_DATETIME)
fun toDateString(dateTime: LocalDateTime) = dateTime.format(FORMATTER_DATE)
}
enum class AnalysisType {
daily, weekly, monthly
}

View File

@@ -0,0 +1,9 @@
package me.jiniworld.demohx.config
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
@Configuration
@ComponentScan("me.jiniworld.demohx.application.notice.port")
class NoticeCoreModule {
}

View File

@@ -0,0 +1,78 @@
spring:
application:
name: chaeking
profiles:
active: local
config:
import:
- chaeking.yml
# - vault://secret/chaeking-local
datasource:
url: jdbc:mariadb://localhost:3306/book
driver-class-name: org.mariadb.jdbc.Driver
username: test
password: test
hikari:
auto-commit: false
connection-test-query: SELECT 1
minimum-idle: 10
maximum-pool-size: 50
# transaction-isolation: TRANSACTION_READ_UNCOMMITTED
pool-name: pool-book
jpa:
database-platform: org.hibernate.dialect.MariaDB103Dialect
properties:
hibernate:
format_sql: false
hbm2ddl.auto: update
implicit_naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
physical_naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
default_batch_fetch_size: 500
open-in-view: false
show-sql: true
devtools:
add-properties: false
server:
port: 8080
tomcat:
basedir: .
# accesslog:
# enabled: true
# directory: logs
# pattern: "%{yyyy-MM-dd HH:mm:ss}t %{X-Forwarded-For}i(%h) %l %u \"%r\" %s %b"
remoteip:
protocol-header: X-Forwarded-Proto
remote-ip-header: X-Forwarded-For
springdoc:
api-docs:
path: /api-docs
default-consumes-media-type: application/json
default-produces-media-type: application/json
swagger-ui:
operations-sorter: alpha
tags-sorter: alpha
path: /
disable-swagger-default-url: true
doc-expansion: none
syntax-highlight:
theme: nord
paths-to-match:
- /v1/**
- /temp/**
- /data4library/**
logging:
# file:
# name: logs/check.log
exception-conversion-word: '%wEx'
pattern:
console: '%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(${LOG_LEVEL_PATTERN:%-5p}){green} %clr([%22thread]){magenta} %clr(%-40.40logger{39}){cyan} %clr(: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}){faint}'
level:
web: debug
org:
springframework:
web:
servlet: debug

View File

@@ -0,0 +1,7 @@
chaeking:
version: 1.0.1
url: http://localhost:${server.port}
book-search:
kakao:
api-url: https://dapi.kakao.com