5 분 소요


  • Let’s create a simple API to verify that module-api properly calls module-core
  • Since we declared Customer and Order entities in module-core, let’s create read/write APIs for Customer and Order in module-api accordingly.
  • Looking at the overall structure, roles might change later (depending on usage…), but let’s simply use Customer and Order

Creating API Module 1 - Adding Dependencies

  • My module-api’s build.gradle.kts is empty as shown below.
      plugins{
    
      }
    
      dependencies{
    
      }
    
  • If you want to use other dependencies in module-api, add them to dependencies in build.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.wool package and create the packages to be used underneath
    • (Not a package, but…) Create ModuleApiApplication.kt and declare the Spring Boot application
      package 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 Responses
    • com.wool.repository : Repository used by API, imports entities declared in module-core for use in Repository
    • com.wool.service : Service used by API, processes data from repository and passes it to controller
  • 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.repository package, and create ModuleCoreCustomerRepository.kt and ModuleCoreOrderRepository.kt
  • I prefixed the repository names with ModuleCore to remember that the entities came from module-core.
  • The interesting thing is that entities declared in module-core can be used in module-api.
  • They can be used as-is without the module-core module name attached.

Creating Repository 1 - ModuleCoreCustomerRepository.kt

  • Inherit from JpaRepository to 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 JPARepository since 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 Service that will inject and use the Repository we just created
  • In the service package, we create dtos because we need to communicate with Repository using data from Controller
  • customer gets CustomerDto, order gets OrderDto and OrderRequestDto
    • OrderRequestDto : The data format received from the controller. Creates customer_id for Customer operations mapped to OrderDto
    • OrderDto : A data class that receives customer data from OrderRequestDto, finds Customer, and maps it to Order

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() : Converts CustomerDto to a Customer entity

Creating Service Dto 2 - OrderRequestDto.kt

  • Create OrderRequestDto.kt that receives data from Controller
    package 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 from OrderRequestDto to find Customer through CustomerRepository

Creating Service Dto 3 - OrderDto.kt

  • A data class that receives a Customer object through OrderRequestDto and maps values to OrderDto
    package 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 ModuleCoreCustomerRepository with CustomerDto and passing it to Controller
    package 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 all Customers through findAll() from ModuleCoreCustomerRepository
    • saveCustomers() : Converts CustomerDto to a Customer entity and saves it to ModuleCoreCustomerRepository

Creating Service 2 - OrderService.kt

  • Responsible for finding Customer through OrderRequestDto received from Controller, mapping it to OrderDto, and saving
    package 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 all Orders through findAll() from ModuleCoreOrderRepository
    • saveOrder() : Finds Customer through OrderRequestDto, maps it to OrderDto, and saves

Creating API Module 5 - Controller

  • Create a Controller that 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() : Passes CustomerDto to CustomerService to save
  • getCustomers() : Gets Customer from CustomerService and 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() : Gets Order from OrderService and returns it
  • saveOrders() : Passes OrderRequestDto to OrderService to save

API Module Configuration - application.yml

  • Various configuration values for running the server are managed in application.yml
  • The values in application.yml were written using postgresql information from docker-compose
  • Create application.yml under the resources package and write as follows
      spring:
        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-core and used them in module-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
      }
    

댓글남기기