🎉 adapter + core 합친 앱 추가
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
package me.jiniworld.demohx.persistence
|
||||
|
||||
import com.ninjasquad.springmockk.MockkBean
|
||||
import me.jiniworld.demohx.config.BeanConfig
|
||||
import me.jiniworld.demohx.persistence.notice.NoticeRepository
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.test.context.jdbc.Sql
|
||||
|
||||
//@DataJpaTest
|
||||
@SpringBootTest
|
||||
//@Import(value = [BeanConfig::class])
|
||||
//@Import(value = [NoticeRepository::class])
|
||||
//@Import([NoticePersistenceAdapter::class])
|
||||
internal class NoticePersistenceAdapterTest {
|
||||
@MockkBean
|
||||
private lateinit var noticeRepository: NoticeRepository
|
||||
// @Test
|
||||
// @Sql("NoticePersistenceAdapterTest.sql")
|
||||
// fun loadNoticesTest() {
|
||||
// val notices = noticePersistenceAdapter.loadNotices(PageRequest.of(0, 10, Sort.by(Sort.Order.desc("id"))))
|
||||
// print(notices)
|
||||
// }
|
||||
|
||||
@Test
|
||||
// @Sql("NoticePersistenceAdapterTest.sql")
|
||||
fun ttt() {
|
||||
print("zzz")
|
||||
val notices = noticeRepository.findAllBy(PageRequest.of(0, 10, Sort.by(Sort.Order.desc("id"))))
|
||||
print(notices)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
insert into notice(id, title, content, created_at) values (3, 'demo-hexagonal', 'hexagonal architecture 구조로 만든 코프링 웹 애플리케이션을 만들었습니다', now());
|
||||
insert into notice(id, title, content, created_at) values (4, 'demo-app 서브 모듈 프로젝트', '이번 프로젝트는 서브 모듈로 구성되어있습니다.', now());
|
||||
12
server/demo-all-in-one-app/build.gradle.kts
Normal file
12
server/demo-all-in-one-app/build.gradle.kts
Normal file
@@ -0,0 +1,12 @@
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
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")
|
||||
implementation("com.epages:restdocs-api-spec-model:0.16.2")
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
|
||||
|
||||
implementation(project(":util:common-util"))
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package me.jiniworld.demohx
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import java.util.*
|
||||
|
||||
@SpringBootApplication
|
||||
class DemoAllInOneAppApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Locale.setDefault(Locale.KOREA)
|
||||
runApplication<DemoAllInOneAppApplication>(*args)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package me.jiniworld.demohx.notice.adapter.`in`.web
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import me.jiniworld.demohx.annotation.WebAdapter
|
||||
import me.jiniworld.demohx.model.DataResponse
|
||||
import me.jiniworld.demohx.model.NotFoundException
|
||||
import me.jiniworld.demohx.notice.application.port.`in`.GetNoticeQuery
|
||||
import me.jiniworld.demohx.notice.application.port.`in`.GetNoticesCommand
|
||||
import org.springframework.web.bind.annotation.*
|
||||
|
||||
@WebAdapter
|
||||
@Tag(name = "setting-system", description = "설정-시스템(공지사항, FAQ, 이용약관, 메타정보 등)")
|
||||
@RestController
|
||||
@RequestMapping("/v1/notices")
|
||||
internal class GetNoticeController(
|
||||
private val getNoticeQuery: GetNoticeQuery,
|
||||
) {
|
||||
|
||||
@Operation(summary = "공지사항 목록")
|
||||
@GetMapping("")
|
||||
fun getNotices(
|
||||
@RequestParam(value = "page", required = false, defaultValue = "0") page: Int,
|
||||
@RequestParam(value = "size", required = false, defaultValue = "10") size: Int,
|
||||
) = getNoticeQuery.getNoticeSimples(GetNoticesCommand(page = page, size = size))
|
||||
?.let { DataResponse(data = it) }
|
||||
?: throw NotFoundException("공지사항이 없습니다.")
|
||||
|
||||
@Operation(summary = "공지사항 상세조회")
|
||||
@GetMapping("/{notice_id}")
|
||||
fun getNotice(@PathVariable("notice_id") noticeId: Long,
|
||||
) = getNoticeQuery.getNoticeDetail(noticeId)
|
||||
?.let { DataResponse(data = it) }
|
||||
?: throw NotFoundException("조회되는 공지사항이 없습니다.")
|
||||
|
||||
@Operation(summary = "test")
|
||||
@GetMapping("/test")
|
||||
fun test() = DataResponse(data = "test")
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.jiniworld.demohx.notice.adapter.out.persistence
|
||||
|
||||
import me.jiniworld.demohx.notice.domain.Notice
|
||||
import org.springframework.data.annotation.CreatedDate
|
||||
import org.springframework.data.annotation.LastModifiedDate
|
||||
import java.time.LocalDateTime
|
||||
import javax.persistence.Entity
|
||||
import javax.persistence.GeneratedValue
|
||||
import javax.persistence.GenerationType
|
||||
import javax.persistence.Table
|
||||
|
||||
@Table(name = "notice")
|
||||
@Entity
|
||||
internal class NoticeEntity {
|
||||
@javax.persistence.Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
var id: Long = 0
|
||||
|
||||
var title: String = ""
|
||||
|
||||
var content: String = ""
|
||||
|
||||
@CreatedDate
|
||||
var createdAt: LocalDateTime = LocalDateTime.now()
|
||||
|
||||
@LastModifiedDate
|
||||
var updatedAt: LocalDateTime? = null
|
||||
|
||||
fun mapToNotice() =
|
||||
Notice(id = id, title = title, content = content, createdAt = createdAt)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package me.jiniworld.demohx.notice.adapter.out.persistence
|
||||
|
||||
import me.jiniworld.demohx.annotation.PersistenceAdapter
|
||||
import me.jiniworld.demohx.notice.application.port.out.LoadNoticePort
|
||||
import me.jiniworld.demohx.notice.domain.Notice
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
|
||||
@PersistenceAdapter
|
||||
internal class NoticePersistenceAdapter(
|
||||
private val noticeRepository: NoticeRepository,
|
||||
) : LoadNoticePort {
|
||||
override fun loadNotices(pageable: Pageable): List<Notice>? {
|
||||
return noticeRepository.findAllBy(pageable).map { it.mapToNotice() }.toList()
|
||||
}
|
||||
|
||||
override fun loadNotice(id: Long): Notice? {
|
||||
return noticeRepository.findByIdOrNull(id)?.mapToNotice()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package me.jiniworld.demohx.notice.adapter.out.persistence
|
||||
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
internal interface NoticeRepository: JpaRepository<NoticeEntity, Long> {
|
||||
fun findAllBy(pageable: Pageable): List<NoticeEntity>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.jiniworld.demohx.notice.application.port.`in`
|
||||
|
||||
import me.jiniworld.demohx.notice.application.port.out.NoticeDetail
|
||||
import me.jiniworld.demohx.notice.application.port.out.NoticeSimple
|
||||
|
||||
interface GetNoticeQuery {
|
||||
fun getNoticeSimples(command: GetNoticesCommand): List<NoticeSimple>?
|
||||
fun getNoticeDetail(id: Long): NoticeDetail?
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package me.jiniworld.demohx.notice.application.port.`in`
|
||||
|
||||
data class GetNoticesCommand(
|
||||
val page: Int,
|
||||
val size: Int,
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.jiniworld.demohx.notice.application.port.out
|
||||
|
||||
import me.jiniworld.demohx.notice.domain.Notice
|
||||
import org.springframework.data.domain.Pageable
|
||||
|
||||
interface LoadNoticePort {
|
||||
fun loadNotices(pageable: Pageable): List<Notice>?
|
||||
fun loadNotice(id: Long): Notice?
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package me.jiniworld.demohx.notice.application.port.out
|
||||
|
||||
data class NoticeDetail(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val content: String,
|
||||
val createdAt: String,
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.jiniworld.demohx.notice.application.port.out
|
||||
|
||||
data class NoticeSimple(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val createdOn: String,
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package me.jiniworld.demohx.notice.application.service
|
||||
|
||||
import me.jiniworld.demohx.annotation.UseCase
|
||||
import me.jiniworld.demohx.notice.application.port.`in`.GetNoticeQuery
|
||||
import me.jiniworld.demohx.notice.application.port.`in`.GetNoticesCommand
|
||||
import me.jiniworld.demohx.notice.application.port.out.LoadNoticePort
|
||||
import me.jiniworld.demohx.notice.application.port.out.NoticeDetail
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@UseCase
|
||||
internal class GetNoticeService(
|
||||
private val loadNoticePort: LoadNoticePort,
|
||||
) : GetNoticeQuery {
|
||||
|
||||
override fun getNoticeSimples(command: GetNoticesCommand) =
|
||||
loadNoticePort.loadNotices(PageRequest.of(command.page, command.size, Sort.by(Sort.Order.desc("id"))))
|
||||
?.map { it.mapToNoticeSimple() }
|
||||
|
||||
override fun getNoticeDetail(id: Long): NoticeDetail? =
|
||||
loadNoticePort.loadNotice(id)?.mapToNoticeDetail()
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package me.jiniworld.demohx.notice.domain
|
||||
|
||||
import me.jiniworld.demohx.DateTimeUtils
|
||||
import me.jiniworld.demohx.notice.application.port.out.NoticeDetail
|
||||
import me.jiniworld.demohx.notice.application.port.out.NoticeSimple
|
||||
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))
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
spring:
|
||||
application:
|
||||
name: demo-all-in-one-app
|
||||
profiles:
|
||||
active: local
|
||||
config:
|
||||
import:
|
||||
- demo-all-in-one-app.yml
|
||||
datasource:
|
||||
url: jdbc:mariadb://localhost:3306/demofx
|
||||
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-demofx
|
||||
jpa:
|
||||
database-platform: org.hibernate.dialect.MariaDB103Dialect
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
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/demo-all-in-one.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
|
||||
7
server/demo-all-in-one-app/src/main/resources/banner.txt
Normal file
7
server/demo-all-in-one-app/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
_ _ _ _
|
||||
_| |___ _____ ___ ___ ___| | |___|_|___ ___ ___ ___ ___
|
||||
| . | -_| | . |___| .'| | |___| | |___| . | | -_|
|
||||
|___|___|_|_|_|___| |__,|_|_| |_|_|_| |___|_|_|___|
|
||||
|
||||
${Ansi.GREEN}:: Spring Boot :: ${Ansi.NORMAL}${spring-boot.formatted-version}${Ansi.NORMAL}
|
||||
${application.title}${application.formatted-version}
|
||||
@@ -0,0 +1,3 @@
|
||||
demo-app:
|
||||
version: 1.0.1
|
||||
url: http://localhost:${server.port}
|
||||
@@ -9,3 +9,5 @@ include("server:demo-app")
|
||||
findProject(":server:demo-app")?.name = "demo-app"
|
||||
include("util:common-util")
|
||||
findProject(":util:common-util")?.name = "common-util"
|
||||
include("server:demo-all-in-one-app")
|
||||
findProject(":server:demo-all-in-one-app")?.name = "demo-all-in-one-app"
|
||||
|
||||
Reference in New Issue
Block a user