🏗️ 프로젝트 구조 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

@@ -3,6 +3,7 @@ plugins {
id("io.spring.dependency-management") version "1.0.13.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
kotlin("plugin.jpa") version "1.6.21"
kotlin("kapt") version "1.6.21"
}

View File

@@ -1,8 +1,8 @@
package me.jiniworld.demohx.notice.domain
package me.jiniworld.demohx.application.notice.domain
import me.jiniworld.demohx.notice.application.port.output.NoticeDetail
import me.jiniworld.demohx.notice.application.port.output.NoticeSimple
import me.jiniworld.demohx.util.DateTimeUtils
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(

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

@@ -1,4 +1,4 @@
package me.jiniworld.demohx.notice.application.port.output
package me.jiniworld.demohx.application.notice.port.output
data class NoticeDetail(
val id: Long,

View File

@@ -1,4 +1,4 @@
package me.jiniworld.demohx.notice.application.port.output
package me.jiniworld.demohx.application.notice.port.output
data class NoticeSimple(
val id: Long,

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

@@ -1,4 +1,4 @@
package me.jiniworld.demohx.util
package me.jiniworld.demohx.application.util
import java.time.LocalDate
import java.time.LocalDateTime

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,7 @@
apply(plugin = "kotlin-jpa")
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
implementation(project(":core:demo-core"))
}

View File

@@ -0,0 +1,17 @@
package me.jiniworld.demohx.config
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
@Configuration
@ComponentScan("me.jiniworld.demohx.persistence")
@EnableJpaRepositories(basePackages = ["me.jiniworld.demohx.persistence.notice.repository"])
@EntityScan(basePackages = ["me.jiniworld.demohx.persistence.notice.entity"])
class NoticeModule {
// @Bean
// fun noticePersistenceAdapter(noticeRepository: NoticeRepository): NoticePersistenceAdapter {
// return NoticePersistenceAdapter(noticeRepository)
// }
}

View File

@@ -0,0 +1,21 @@
package me.jiniworld.demohx.persistence.notice
import me.jiniworld.demohx.application.notice.port.output.LoadNoticePort
import me.jiniworld.demohx.application.notice.domain.Notice
import me.jiniworld.demohx.persistence.notice.repository.NoticeRepository
import org.springframework.data.domain.Pageable
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
@Component
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

@@ -1,6 +1,6 @@
package me.jiniworld.demohx.notice.adapter.output.persistence
package me.jiniworld.demohx.persistence.notice.entity
import me.jiniworld.demohx.notice.domain.Notice
import me.jiniworld.demohx.application.notice.domain.Notice
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import java.time.LocalDateTime

View File

@@ -1,5 +1,6 @@
package me.jiniworld.demohx.notice.adapter.output.persistence
package me.jiniworld.demohx.persistence.notice.repository
import me.jiniworld.demohx.persistence.notice.entity.NoticeEntity
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository

View File

@@ -0,0 +1,78 @@
spring:
application:
name: demo-datastore-mariadb
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,11 @@
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")
implementation(project(":core:demo-core"))
implementation(project(":infrastructure:datastore-mariadb"))
}

View File

@@ -4,10 +4,14 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import java.util.*
@SpringBootApplication
class DemoHxApplication
@SpringBootApplication(scanBasePackages = [
"me.jiniworld.demohx.config",
"me.jiniworld.demohx.web",
"me.jiniworld.demohx.application"
])
class DemoAppApplication
fun main(args: Array<String>) {
Locale.setDefault(Locale.KOREA)
runApplication<DemoHxApplication>(*args)
runApplication<DemoAppApplication>(*args)
}

View File

@@ -1,16 +1,14 @@
package me.jiniworld.demohx.notice.adapter.input.web
package me.jiniworld.demohx.web.notice
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import me.jiniworld.demohx.notice.application.service.GetNoticeService
import me.jiniworld.demohx.notice.domain.Notice
import me.jiniworld.demohx.application.notice.port.input.GetNoticeQuery
import me.jiniworld.demohx.application.notice.domain.Notice
import me.jiniworld.demohx.application.notice.port.output.NoticeSimple
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
@Component
//@WebAdapter
@@ -18,7 +16,7 @@ import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/v1/notices")
internal class GetNoticeController(
private val getNoticeQuery: GetNoticeService,
private val getNoticeQuery: GetNoticeQuery,
) {
@Operation(summary = "공지사항 목록")
@@ -26,9 +24,11 @@ internal class GetNoticeController(
fun notices(
@RequestParam(value = "page", required = false, defaultValue = "0") page: Int,
@RequestParam(value = "size", required = false, defaultValue = "10") size: Int,
): List<Notice> {
println(">>>>>>>>> 1")
return getNoticeQuery.getNoticeSimple(PageRequest.of(page, size, Sort.by(Sort.Order.desc("id"))))
}
) = getNoticeQuery.getNoticeSimple(PageRequest.of(page, size, Sort.by(Sort.Order.desc("id"))))
@Operation(summary = "공지사항 상세조회")
@GetMapping("/{notice_id}")
fun notice(@PathVariable("notice_id") noticeId: Long) =
getNoticeQuery.getNoticeDetail(noticeId)
}

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

View File

@@ -1,3 +1,9 @@
rootProject.name = "demo-hexagonal"
include("web")
include("core:demo-core")
findProject(":core:demo-core")?.name = "demo-core"
include("infrastructure:datastore-mariadb")
findProject(":infrastructure:datastore-mariadb")?.name = "datastore-mariadb"
include("server")
include("server:demo-app")
findProject(":server:demo-app")?.name = "demo-app"

View File

@@ -1,21 +0,0 @@
package me.jiniworld.demohx.notice.adapter.output.persistence
import me.jiniworld.demohx.notice.application.port.output.LoadNoticePort
import me.jiniworld.demohx.notice.domain.Notice
import org.springframework.data.domain.Pageable
import java.time.LocalDateTime
internal class NoticePersistenceAdapter(
private val noticeRepository: NoticeRepository,
) : LoadNoticePort {
override fun loadNotices(pageable: Pageable): List<Notice> {
println(">>>>>>>>> 3")
return noticeRepository.findAllBy(pageable).map { it.mapToNotice() }.toList()
}
override fun loadNotice(id: Long): Notice? {
return Notice(id = id, title = "zzz", content = "fff", createdAt = LocalDateTime.now())
// return noticeRepository.findById(id)?.map(NoticeEntity::mapToNotice)
}
}

View File

@@ -1,15 +0,0 @@
package me.jiniworld.demohx.notice.adapter.output.persistence
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
@Configuration
@EnableJpaRepositories
internal class NoticePersistenceAdapterConfiguration {
@Bean
fun noticePersistenceAdapter(noticeRepository: NoticeRepository): NoticePersistenceAdapter {
return NoticePersistenceAdapter(noticeRepository)
}
}

View File

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

View File

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

View File

@@ -1,27 +0,0 @@
package me.jiniworld.demohx.notice.application.service
import me.jiniworld.demohx.notice.application.port.input.GetNoticeQuery
import me.jiniworld.demohx.notice.application.port.output.LoadNoticePort
import me.jiniworld.demohx.notice.domain.Notice
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import java.sql.DriverManager.println
@Transactional(readOnly = true)
@Component
internal class GetNoticeService(
private val loadNoticePort: LoadNoticePort,
) : GetNoticeQuery {
override fun getNoticeSimple(pageable: Pageable): List<Notice> {
println(">>>>>>>>> 2")
return loadNoticePort.loadNotices(pageable)
// return loadNoticePort.loadNotices(pageable).map { it.mapToNoticeSimple() }.toList()
}
override fun getNoticeDetail(id: Long): Notice? {
return loadNoticePort.loadNotice(id)
// return loadNoticePort.loadNotice(id)?.mapToNoticeDetail()
}
}