🎉 adapter + core 합친 앱 추가

This commit is contained in:
jini
2022-12-02 05:19:05 +09:00
parent 7a260d90a9
commit 079d1ec338
19 changed files with 337 additions and 0 deletions

View File

@@ -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)
}
}

View File

@@ -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());

View 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"))
}

View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -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()
}
}

View File

@@ -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>
}

View File

@@ -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?
}

View File

@@ -0,0 +1,6 @@
package me.jiniworld.demohx.notice.application.port.`in`
data class GetNoticesCommand(
val page: Int,
val size: Int,
)

View File

@@ -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?
}

View File

@@ -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,
)

View File

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

View File

@@ -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()
}

View File

@@ -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))
}

View File

@@ -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

View File

@@ -0,0 +1,7 @@
_ _ _ _
_| |___ _____ ___ ___ ___| | |___|_|___ ___ ___ ___ ___
| . | -_| | . |___| .'| | |___| | |___| . | | -_|
|___|___|_|_|_|___| |__,|_|_| |_|_|_| |___|_|_|___|
${Ansi.GREEN}:: Spring Boot :: ${Ansi.NORMAL}${spring-boot.formatted-version}${Ansi.NORMAL}
${application.title}${application.formatted-version}

View File

@@ -0,0 +1,3 @@
demo-app:
version: 1.0.1
url: http://localhost:${server.port}

View File

@@ -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"