스프링부트 멀티모듈 구성하기(3) - API 모듈 작성하기 Spring Boot Multi-Module Setup (3) - Creating API Module
- Let’s create a simple API to verify that
module-apiproperly callsmodule-core - Since we declared
CustomerandOrderentities inmodule-core, let’s create read/write APIs forCustomerandOrderinmodule-apiaccordingly. - Looking at the overall structure, roles might change later (depending on usage…), but let’s simply use
CustomerandOrder
Creating API Module 1 - Adding Dependencies
- My
module-api’sbuild.gradle.ktsis empty as shown below.plugins{ } dependencies{ } - If you want to use other dependencies in
module-api, add them todependenciesinbuild.gradle.kts
Creating API Module 2 - Package Setup
- As mentioned in
module-core, to use multi-module, the base package name must be the same. - Create the
com.woolpackage and create the packages to be used underneath- (Not a package, but…) Create
ModuleApiApplication.ktand declare the Spring Boot applicationpackage com.wool import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication @SpringBootApplication class ModuleApiApplication fun main(args: Array<String>) { runApplication<ModuleApiApplication>(*args) } com.wool.controller: API Controller, responsible for receiving external Requests and sending Responsescom.wool.repository: Repository used by API, imports entities declared inmodule-corefor use in Repositorycom.wool.service: Service used by API, processes data fromrepositoryand passes it tocontroller
- (Not a package, but…) Create
- There may also be various DTOs. Let’s look at these as we create them.
Creating API Module 3 - Creating Repository
- Create the
com.wool.repositorypackage, and createModuleCoreCustomerRepository.ktandModuleCoreOrderRepository.kt - I prefixed the repository names with
ModuleCoreto remember that the entities came frommodule-core. - The interesting thing is that entities declared in
module-corecan be used inmodule-api. - They can be used as-is without the
module-coremodule name attached. 
Creating Repository 1 - ModuleCoreCustomerRepository.kt
- Inherit from
JpaRepositoryto use JPA.// com.wool.repository.ModuleCoreCustomerRepository.kt package com.wool.repository import com.wool.entity.Customer import org.springframework.data.jpa.repository.JpaRepository interface ModuleCoreCustomerRepository : JpaRepository<Customer, Long> { }
Creating Repository 2 - ModuleCoreOrderRepository.kt
- Similarly, inherit from
JPARepositorysince we’re using JPA.package com.wool.repository import com.wool.entity.Customer import org.springframework.data.jpa.repository.JpaRepository interface ModuleCoreCustomerRepository:JpaRepository<Customer, Long> { }
Creating API Module 4 - Creating Service
- Now let’s create a
Servicethat will inject and use theRepositorywe just created - In the
servicepackage, we createdtos because we need to communicate withRepositoryusing data fromController customergetsCustomerDto,ordergetsOrderDtoandOrderRequestDtoOrderRequestDto: The data format received from the controller. Createscustomer_idforCustomeroperations mapped toOrderDtoOrderDto: A data class that receives customer data fromOrderRequestDto, findsCustomer, and maps it toOrder

Creating Service Dto 1 - CustomerDto.kt
- A data class responsible for converting Customer data to an entity
package com.wool.service.dtos.customer import com.wool.entity.Customer data class CustomerDto( val customerNickName: String, val customerAddress: String, ) { fun toEntity() = Customer( customerNickName = this.customerNickName, customerAddress = this.customerAddress ) }toEntity(): ConvertsCustomerDtoto aCustomerentity
Creating Service Dto 2 - OrderRequestDto.kt
- Create
OrderRequestDto.ktthat receives data from Controllerpackage com.wool.service.dtos.order data class OrderRequestDto( val orderStoreName: String, val orderStoreAddress: String, val orderItem: String, val orderPrice: Int, val customerId: Long, )customerId: Value extracted fromOrderRequestDtoto findCustomerthroughCustomerRepository
Creating Service Dto 3 - OrderDto.kt
- A data class that receives a
Customerobject throughOrderRequestDtoand maps values toOrderDtopackage com.wool.service.dtos.order import com.wool.entity.Customer import com.wool.entity.Order data class OrderDto( val orderStoreName: String, val orderStoreAddress: String, val orderItem: String, val orderPrice: Int, val customer: Customer, ){ fun toEntity() = Order( orderStoreName = this.orderStoreName, orderStoreAddress = this.orderStoreAddress, orderItem = this.orderItem, orderPrice = this.orderPrice, customer = this.customer ) }
Creating Service 1 - CustomerService.kt
- Responsible for combining data communicated from
ModuleCoreCustomerRepositorywithCustomerDtoand passing it to Controllerpackage com.wool.service import com.wool.repository.ModuleCoreCustomerRepository import com.wool.service.dtos.customer.CustomerDto import org.springframework.stereotype.Service @Service class CustomerService( private val customerRepository: ModuleCoreCustomerRepository ) { fun getCustomers() = customerRepository.findAll() fun saveCustomers(customerDto: CustomerDto) { this.customerRepository.save(customerDto.toEntity()) } }getCustomers(): Gets allCustomers throughfindAll()fromModuleCoreCustomerRepositorysaveCustomers(): ConvertsCustomerDtoto aCustomerentity and saves it toModuleCoreCustomerRepository
Creating Service 2 - OrderService.kt
- Responsible for finding
CustomerthroughOrderRequestDtoreceived fromController, mapping it toOrderDto, and savingpackage com.wool.service import com.wool.entity.Customer import com.wool.repository.ModuleCoreCustomerRepository import com.wool.repository.ModuleCoreOrderRepository import com.wool.service.dtos.order.OrderRequestDto import com.wool.service.dtos.order.OrderDto import org.springframework.stereotype.Service @Service class OrderService( private val orderRepository: ModuleCoreOrderRepository, private val customerRepository: ModuleCoreCustomerRepository ) { fun getOrders() = orderRepository.findAll() fun saveOrder(orderRequestDto: OrderRequestDto) { val customer: Customer = customerRepository.findById(orderRequestDto.customerId).get() //if customer exists if (customer != null) { val orderDto = OrderDto( orderStoreName = orderRequestDto.orderStoreName, orderStoreAddress = orderRequestDto.orderStoreAddress, orderItem = orderRequestDto.orderItem, orderPrice = orderRequestDto.orderPrice, customer = customer ) orderRepository.save(orderDto.toEntity()) } else { //if customer doesn't exist throw Exception("Customer not found.") } } }getOrders(): Gets allOrders throughfindAll()fromModuleCoreOrderRepositorysaveOrder(): FindsCustomerthroughOrderRequestDto, maps it toOrderDto, and saves
Creating API Module 5 - Controller
- Create a
Controllerthat can receive external requests - Since the functionality isn’t detailed, there isn’t much to see in the controller beyond DTO configuration

Creating Controller 1 - CustomerController.kt
package com.wool.controller
import com.wool.service.CustomerService
import com.wool.service.OrderService
import com.wool.service.dtos.customer.CustomerDto
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
@RestController
class CustomerController(
private val customerService: CustomerService
) {
@PostMapping("/customers")
fun saveCustomers(@RequestBody customerDto: CustomerDto) {
return this.customerService.saveCustomers(customerDto)
}
@GetMapping("/customers")
fun getCustomers() = this.customerService.getCustomers()
}
saveCustomers(): PassesCustomerDtotoCustomerServiceto savegetCustomers(): GetsCustomerfromCustomerServiceand returns it
Creating Controller 2 - OrderController.kt
package com.wool.controller
import com.wool.service.OrderService
import com.wool.service.dtos.order.OrderRequestDto
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
@RestController
class OrderController(
private val orderService: OrderService
) {
@GetMapping("/orders")
fun getOrders() = orderService.getOrders()
@PostMapping("/orders")
fun saveOrders(@RequestBody orderRequestDto: OrderRequestDto) {
//save order
return orderService.saveOrder(orderRequestDto)
}
}
getOrders(): GetsOrderfromOrderServiceand returns itsaveOrders(): PassesOrderRequestDtotoOrderServiceto save
API Module Configuration - application.yml
- Various configuration values for running the server are managed in
application.yml - The values in
application.ymlwere written using postgresql information from docker-compose - Create
application.ymlunder theresourcespackage and write as followsspring: datasource: hikari: pool-name: HikariCp maximum-pool-size: 2 minimum-idle: 2 username: wool password: password1234 url: jdbc:postgresql://localhost:9876/wooldb driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: format_sql: true default_schema: springtest jackson: serialization: fail-on-empty-beans: false
Wrapping Up API Module
- In this post, we imported entities written in
module-coreand used them inmodule-api - When you actually run the server and test, data retrieval/storage works well
- After running
ModuleApiApplication.kt, test with the API test data below
API Testing
Customer Retrieval
- URL :
http://localhost:8080/customers - Method :
GET - Response
[ { "customerId": 1, "customerNickName": "Oliver", "customerAddress": "Want to live in Seoul", "createdAt": "2022-11-20T00:00:00", "updatedAt": "2022-11-20T00:00:00" }, { "customerId": 2, "customerNickName": "Future", "customerAddress": "Successful life in Jeongja-dong", "createdAt": "2022-11-21T00:00:00", "updatedAt": "2022-11-21T00:00:00" } ]
Customer Creation
- URL :
http://localhost:8080/customers - Method :
POST - Body :
application/json{ "customerNickName": "Future", "customerAddress": "Successful life in Jeongja-dong" }
Order Retrieval
- URL :
http://localhost:8080/orders - Method :
GET - Response
[ { "orderId": 2, "orderUUID": "014e018a-3391-4c54-a913-3230e65a0013", "orderStoreName": "Yam Yam Gimbap", "orderStoreAddress": "Gimbap-dong, Yamyam-ro, Seoul", "orderItem": "Bulgogi Tuna Gimbap", "orderPrice": 6500, "customer": { "customerId": 1, "customerNickName": "Oliver", "customerAddress": "Want to live in Seoul", "createdAt": "2022-11-20T00:00:00", "updatedAt": "2022-11-20T00:00:00" }, "createdAt": "2022-11-21T00:00:00", "updatedAt": "2022-11-21T00:00:00" }, { "orderId": 3, "orderUUID": "caf72de8-7174-48d9-9e06-d5665ca2225e", "orderStoreName": "Yam Yam Gimbap", "orderStoreAddress": "Gimbap-dong, Yamyam-ro, Seoul", "orderItem": "Bulgogi Tuna Gimbap", "orderPrice": 6500, "customer": { "customerId": 2, "customerNickName": "Future", "customerAddress": "Successful life in Jeongja-dong", "createdAt": "2022-11-21T00:00:00", "updatedAt": "2022-11-21T00:00:00" }, "createdAt": "2022-11-21T00:00:00", "updatedAt": "2022-11-21T00:00:00" } ]
Order Creation
- URL :
http://localhost:8080/orders - Method :
POST - Body :
application/json{ "orderStoreName": "Yam Yam Gimbap", "orderStoreAddress": "Gimbap-dong, Yamyam-ro, Seoul", "orderItem": "Bulgogi Tuna Gimbap", "orderPrice": 6500, "customerId": 2 }
댓글남기기