move and refactor city and product services to core

This commit is contained in:
Savosin Denis
2025-05-06 14:09:01 +07:00
parent b855aba506
commit f92ba7795d
18 changed files with 264 additions and 202 deletions

View File

@@ -2,6 +2,9 @@ group = "com.github.dannecron.demo"
version = "single-version" version = "single-version"
dependencies { dependencies {
implementation(project(":db"))
implementation(rootProject.libs.spring.boot.starter.jdbc)
implementation(rootProject.libs.spring.boot.starter.validation) implementation(rootProject.libs.spring.boot.starter.validation)
implementation(rootProject.libs.json.schema.validator) implementation(rootProject.libs.json.schema.validator)
} }

View File

@@ -0,0 +1,15 @@
package com.github.dannecron.demo.core.dto
import java.time.OffsetDateTime
import java.util.UUID
data class City(
val id: Long,
val guid: UUID,
val name: String,
val createdAt: OffsetDateTime,
val updatedAt: OffsetDateTime?,
val deletedAt: OffsetDateTime?,
) {
fun isDeleted(): Boolean = deletedAt != null
}

View File

@@ -0,0 +1,11 @@
package com.github.dannecron.demo.core.dto
import java.time.OffsetDateTime
data class CityCreate(
val guid: String,
val name: String,
val createdAt: OffsetDateTime,
val updatedAt: OffsetDateTime?,
val deletedAt: OffsetDateTime?,
)

View File

@@ -0,0 +1,26 @@
package com.github.dannecron.demo.core.dto
import java.time.OffsetDateTime
import java.util.UUID
import kotlin.math.pow
import kotlin.math.roundToInt
data class Product(
val id: Long,
val guid: UUID,
val name: String,
val description: String?,
val price: Long,
val createdAt: OffsetDateTime,
val updatedAt: OffsetDateTime?,
val deletedAt: OffsetDateTime?,
) {
fun getPriceDouble(): Double = (price.toDouble() / 100).roundTo(2)
fun isDeleted(): Boolean = deletedAt != null
private fun Double.roundTo(numFractionDigits: Int): Double {
val factor = 10.0.pow(numFractionDigits.toDouble())
return (this * factor).roundToInt() / factor
}
}

View File

@@ -0,0 +1,3 @@
package com.github.dannecron.demo.core.exceptions
class AlreadyDeletedException: RuntimeException()

View File

@@ -0,0 +1,3 @@
package com.github.dannecron.demo.core.exceptions
class CityNotFoundException: ModelNotFoundException("city")

View File

@@ -0,0 +1,3 @@
package com.github.dannecron.demo.core.exceptions
open class ModelNotFoundException(entityName: String): RuntimeException("$entityName not found")

View File

@@ -0,0 +1,3 @@
package com.github.dannecron.demo.core.exceptions
class ProductNotFoundException: ModelNotFoundException("product")

View File

@@ -0,0 +1,19 @@
package com.github.dannecron.demo.core.services.city
import com.github.dannecron.demo.core.dto.City
import com.github.dannecron.demo.core.dto.CityCreate
import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.core.exceptions.CityNotFoundException
import org.springframework.stereotype.Service
import java.util.UUID
@Service
interface CityService {
fun findByGuid(guid: UUID): City?
fun create(name: String): City
fun create(cityCreate: CityCreate): City
@Throws(CityNotFoundException::class, AlreadyDeletedException::class)
fun delete(guid: UUID): City
}

View File

@@ -0,0 +1,71 @@
package com.github.dannecron.demo.core.services.city
import com.github.dannecron.demo.core.dto.City
import com.github.dannecron.demo.core.dto.CityCreate
import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.core.exceptions.CityNotFoundException
import com.github.dannecron.demo.core.services.generation.CommonGenerator
import com.github.dannecron.demo.db.entity.CityEntity
import com.github.dannecron.demo.db.repository.CityRepository
import org.springframework.stereotype.Service
import java.time.OffsetDateTime
import java.util.UUID
@Service
class CityServiceImpl(
private val cityRepository: CityRepository,
private val commonGenerator: CommonGenerator,
): CityService {
override fun findByGuid(guid: UUID): City? = cityRepository.findByGuid(guid)?.toCore()
override fun create(name: String): City = CityEntity(
id = null,
guid = commonGenerator.generateUUID(),
name = name,
createdAt = commonGenerator.generateCurrentTime(),
updatedAt = null,
deletedAt = null,
).let(cityRepository::save).toCore()
override fun create(cityCreate: CityCreate): City = CityEntity(
id = null,
guid = UUID.fromString(cityCreate.guid),
name = cityCreate.name,
createdAt = cityCreate.createdAt,
updatedAt = cityCreate.updatedAt,
deletedAt = cityCreate.deletedAt,
).let(cityRepository::save).toCore()
@Throws(CityNotFoundException::class, AlreadyDeletedException::class)
override fun delete(guid: UUID): City {
val city = findByGuid(guid) ?: throw CityNotFoundException()
if (city.isDeleted()) {
throw AlreadyDeletedException()
}
return cityRepository.save(
city.copy(
deletedAt = OffsetDateTime.now(),
).toEntity()
).toCore()
}
private fun CityEntity.toCore() = City(
id = id!!,
guid = guid,
name = name,
createdAt = createdAt,
updatedAt = updatedAt,
deletedAt = deletedAt,
)
private fun City.toEntity() = CityEntity(
id = id,
guid = guid,
name = name,
createdAt = createdAt,
updatedAt = updatedAt,
deletedAt = deletedAt,
)
}

View File

@@ -0,0 +1,19 @@
package com.github.dannecron.demo.core.services.product
import com.github.dannecron.demo.core.dto.Product
import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.core.exceptions.ProductNotFoundException
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import java.util.UUID
interface ProductService {
fun findByGuid(guid: UUID): Product?
fun findAll(pageable: Pageable): Page<Product>
fun create(name: String, price: Long, description: String?): Product
@Throws(ProductNotFoundException::class, AlreadyDeletedException::class)
fun delete(guid: UUID): Product
}

View File

@@ -1,30 +1,26 @@
package com.github.dannecron.demo.services.database.product package com.github.dannecron.demo.core.services.product
import com.github.dannecron.demo.core.dto.Product
import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.core.exceptions.ProductNotFoundException
import com.github.dannecron.demo.core.services.generation.CommonGenerator import com.github.dannecron.demo.core.services.generation.CommonGenerator
import com.github.dannecron.demo.db.entity.Product import com.github.dannecron.demo.core.utils.LoggerDelegate
import com.github.dannecron.demo.db.entity.ProductEntity
import com.github.dannecron.demo.db.repository.ProductRepository import com.github.dannecron.demo.db.repository.ProductRepository
import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.services.database.exceptions.ProductNotFoundException
import com.github.dannecron.demo.services.kafka.Producer
import com.github.dannecron.demo.services.kafka.dto.ProductDto
import com.github.dannecron.demo.services.kafka.exceptions.InvalidArgumentException
import com.github.dannecron.demo.utils.LoggerDelegate
import net.logstash.logback.marker.Markers import net.logstash.logback.marker.Markers
import org.springframework.data.domain.Page import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.format.DateTimeFormatter
import java.util.UUID import java.util.UUID
@Service @Service
class ProductServiceImpl( class ProductServiceImpl(
private val productRepository: ProductRepository, private val productRepository: ProductRepository,
private val producer: Producer,
private val commonGenerator: CommonGenerator, private val commonGenerator: CommonGenerator,
): ProductService { ): ProductService {
private val logger by LoggerDelegate() private val logger by LoggerDelegate()
override fun findByGuid(guid: UUID): Product? = productRepository.findByGuid(guid) override fun findByGuid(guid: UUID): Product? = productRepository.findByGuid(guid)?.toCore()
.also { .also {
logger.debug( logger.debug(
Markers.appendEntries(mapOf("guid" to guid, "idResult" to it?.id)), Markers.appendEntries(mapOf("guid" to guid, "idResult" to it?.id)),
@@ -33,9 +29,10 @@ class ProductServiceImpl(
} }
override fun findAll(pageable: Pageable): Page<Product> = productRepository.findAll(pageable) override fun findAll(pageable: Pageable): Page<Product> = productRepository.findAll(pageable)
.map { it.toCore() }
override fun create(name: String, price: Long, description: String?): Product { override fun create(name: String, price: Long, description: String?): Product =
val product = Product( ProductEntity(
id = null, id = null,
guid = commonGenerator.generateUUID(), guid = commonGenerator.generateUUID(),
name = name, name = name,
@@ -44,11 +41,9 @@ class ProductServiceImpl(
createdAt = commonGenerator.generateCurrentTime(), createdAt = commonGenerator.generateCurrentTime(),
updatedAt = null, updatedAt = null,
deletedAt = null, deletedAt = null,
) ).let(productRepository::save).toCore()
return productRepository.save(product)
}
@Throws(ProductNotFoundException::class, AlreadyDeletedException::class)
override fun delete(guid: UUID): Product { override fun delete(guid: UUID): Product {
val product = findByGuid(guid) ?: throw ProductNotFoundException() val product = findByGuid(guid) ?: throw ProductNotFoundException()
@@ -56,27 +51,32 @@ class ProductServiceImpl(
throw AlreadyDeletedException() throw AlreadyDeletedException()
} }
val deletedProduct = product.copy( return product.copy(
deletedAt = commonGenerator.generateCurrentTime(), deletedAt = commonGenerator.generateCurrentTime(),
) ).toEntity()
.let(productRepository::save)
return productRepository.save(deletedProduct) .toCore()
} }
override fun syncToKafka(guid: UUID, topic: String?) { private fun ProductEntity.toCore() = Product(
val product = findByGuid(guid) ?: throw ProductNotFoundException() id = id!!,
guid = guid,
producer.produceProductSync(product.toKafkaDto())
}
private fun Product.toKafkaDto() = ProductDto(
id = id ?: throw InvalidArgumentException("product.id"),
guid = guid.toString(),
name = name, name = name,
description = description, description = description,
price = price, price = price,
createdAt = createdAt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), createdAt = createdAt,
updatedAt = updatedAt?.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), updatedAt = updatedAt,
deletedAt = deletedAt?.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), deletedAt = deletedAt,
)
private fun Product.toEntity() = ProductEntity(
id = id,
guid = guid,
name = name,
description = description,
price = price,
createdAt = createdAt,
updatedAt = updatedAt,
deletedAt = deletedAt,
) )
} }

View File

@@ -1,4 +1,4 @@
package com.github.dannecron.demo.utils package com.github.dannecron.demo.core.utils
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory

View File

@@ -1,9 +1,10 @@
package com.github.dannecron.demo.services.database.city package com.github.dannecron.demo.core.services.city
import com.github.dannecron.demo.core.dto.City
import com.github.dannecron.demo.core.dto.CityCreate
import com.github.dannecron.demo.core.services.generation.CommonGenerator import com.github.dannecron.demo.core.services.generation.CommonGenerator
import com.github.dannecron.demo.db.entity.City import com.github.dannecron.demo.db.entity.CityEntity
import com.github.dannecron.demo.db.repository.CityRepository import com.github.dannecron.demo.db.repository.CityRepository
import com.github.dannecron.demo.services.kafka.dto.CityCreateDto
import org.mockito.kotlin.any import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
@@ -11,7 +12,6 @@ import org.mockito.kotlin.times
import org.mockito.kotlin.verify import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever import org.mockito.kotlin.whenever
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import java.util.UUID import java.util.UUID
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -27,6 +27,14 @@ class CityServiceImplTest {
private val cityRepository: CityRepository = mock() private val cityRepository: CityRepository = mock()
private val cityServiceImpl = CityServiceImpl(cityRepository, commonGenerator) private val cityServiceImpl = CityServiceImpl(cityRepository, commonGenerator)
private val cityEntity = CityEntity(
id = 1000,
guid = mockGuid,
name = "name",
createdAt = mockCurrentTime,
updatedAt = null,
deletedAt = null,
)
private val city = City( private val city = City(
id = 1000, id = 1000,
guid = mockGuid, guid = mockGuid,
@@ -38,33 +46,34 @@ class CityServiceImplTest {
@Test @Test
fun `create - by name`() { fun `create - by name`() {
whenever(cityRepository.save(any<City>())).thenReturn(city) whenever(cityRepository.save(any<CityEntity>())).thenReturn(cityEntity)
val result = cityServiceImpl.create("name") val result = cityServiceImpl.create("name")
assertEquals(city, result) assertEquals(city, result)
verify(cityRepository, times(1)).save(city.copy(id = null)) verify(cityRepository, times(1)).save(cityEntity.copy(id = null))
} }
@Test @Test
fun `create - by dto`() { fun `create - by dto`() {
val cityGuid = UUID.randomUUID() val cityGuid = UUID.randomUUID()
val createdAt = OffsetDateTime.now() val createdAt = OffsetDateTime.now()
val cityCreate = CityCreateDto( val cityCreate = CityCreate(
guid = cityGuid.toString(), guid = cityGuid.toString(),
name = "name", name = "name",
createdAt = createdAt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), createdAt = createdAt,
updatedAt = null, updatedAt = null,
deletedAt = null, deletedAt = null,
) )
val expectedCityEntity = cityEntity.copy(guid = cityGuid, createdAt = createdAt)
val expectedCity = city.copy(guid = cityGuid, createdAt = createdAt) val expectedCity = city.copy(guid = cityGuid, createdAt = createdAt)
whenever(cityRepository.save(any<City>())).thenReturn(expectedCity) whenever(cityRepository.save(any<CityEntity>())).thenReturn(expectedCityEntity)
val result = cityServiceImpl.create(cityCreate) val result = cityServiceImpl.create(cityCreate)
assertEquals(expectedCity, result) assertEquals(expectedCity, result)
verify(cityRepository, times(1)).save(expectedCity.copy(id = null)) verify(cityRepository, times(1)).save(expectedCityEntity.copy(id = null))
} }
} }

View File

@@ -1,12 +1,11 @@
package com.github.dannecron.demo.services.database.product package com.github.dannecron.demo.core.services.product
import com.github.dannecron.demo.core.dto.Product
import com.github.dannecron.demo.core.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.core.exceptions.ProductNotFoundException
import com.github.dannecron.demo.core.services.generation.CommonGenerator import com.github.dannecron.demo.core.services.generation.CommonGenerator
import com.github.dannecron.demo.db.entity.Product import com.github.dannecron.demo.db.entity.ProductEntity
import com.github.dannecron.demo.db.repository.ProductRepository import com.github.dannecron.demo.db.repository.ProductRepository
import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.services.database.exceptions.ProductNotFoundException
import com.github.dannecron.demo.services.kafka.Producer
import com.github.dannecron.demo.services.kafka.dto.ProductDto
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.mockito.kotlin.any import org.mockito.kotlin.any
@@ -15,10 +14,8 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never import org.mockito.kotlin.never
import org.mockito.kotlin.times import org.mockito.kotlin.times
import org.mockito.kotlin.verify import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.whenever import org.mockito.kotlin.whenever
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import java.util.UUID import java.util.UUID
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -27,7 +24,6 @@ class ProductServiceImplTest {
private val mockCurrentTime = OffsetDateTime.now() private val mockCurrentTime = OffsetDateTime.now()
private val productRepository: ProductRepository = mock() private val productRepository: ProductRepository = mock()
private val producer: Producer = mock()
private val commonGenerator: CommonGenerator = mock { private val commonGenerator: CommonGenerator = mock {
on { generateUUID() } doReturn mockGuid on { generateUUID() } doReturn mockGuid
on { generateCurrentTime() } doReturn mockCurrentTime on { generateCurrentTime() } doReturn mockCurrentTime
@@ -35,72 +31,79 @@ class ProductServiceImplTest {
private val productService = ProductServiceImpl( private val productService = ProductServiceImpl(
productRepository = productRepository, productRepository = productRepository,
producer = producer,
commonGenerator = commonGenerator, commonGenerator = commonGenerator,
) )
private val guid = UUID.randomUUID() private val guid = UUID.randomUUID()
private val productEntity = ProductEntity(
id = 123,
guid = guid,
name = "name",
description = "description",
price = 10050,
createdAt = mockCurrentTime.minusDays(1),
updatedAt = mockCurrentTime.minusHours(2),
deletedAt = null,
)
private val product = Product( private val product = Product(
id = 123, id = 123,
guid = guid, guid = guid,
name = "name", name = "name",
description = "description", description = "description",
price = 10050, price = 10050,
createdAt = OffsetDateTime.now().minusDays(1), createdAt = mockCurrentTime.minusDays(1),
updatedAt = OffsetDateTime.now().minusHours(2), updatedAt = mockCurrentTime.minusHours(2),
deletedAt = null,
)
private val kafkaProductDto = ProductDto(
id = 123,
guid = guid.toString(),
name = "name",
description = "description",
price = 10050,
createdAt = product.createdAt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME),
updatedAt = product.updatedAt!!.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME),
deletedAt = null, deletedAt = null,
) )
@Test @Test
fun create() { fun create() {
val expectedProductForCreation = product.copy( val expectedProductForCreation = productEntity.copy(
id = null, id = null,
guid = mockGuid, guid = mockGuid,
createdAt = mockCurrentTime, createdAt = mockCurrentTime,
updatedAt = null, updatedAt = null,
) )
val expectedCreatedProduct = expectedProductForCreation.copy(id = 1) val expectedCreatedProductEntity = expectedProductForCreation.copy(id = 1)
val expectedProduct = product.copy(
id = 1,
guid = mockGuid,
createdAt = mockCurrentTime,
updatedAt = null,
)
whenever(productRepository.save<Product>(any())).thenReturn(expectedCreatedProduct) whenever(productRepository.save<ProductEntity>(any())).thenReturn(expectedCreatedProductEntity)
val result = productService.create( val result = productService.create(
name = "name", name = "name",
price = 10050, price = 10050,
description = "description", description = "description",
) )
assertEquals(expectedCreatedProduct, result) assertEquals(expectedProduct, result)
verify(productRepository, times(1)).save(expectedProductForCreation) verify(productRepository, times(1)).save(expectedProductForCreation)
} }
@Test @Test
fun `delete - success`() { fun `delete - success`() {
val deletedProduct = product.copy( val deletedProductEntity = productEntity.copy(
deletedAt = mockCurrentTime, deletedAt = mockCurrentTime,
) )
whenever(productRepository.findByGuid(any())).thenReturn(product) val expectedProduct = product.copy(deletedAt = mockCurrentTime)
whenever(productRepository.save<Product>(any())).thenReturn(deletedProduct)
whenever(productRepository.findByGuid(any())).thenReturn(productEntity)
whenever(productRepository.save<ProductEntity>(any())).thenReturn(deletedProductEntity)
val result = productService.delete(guid) val result = productService.delete(guid)
assertEquals(deletedProduct, result) assertEquals(expectedProduct, result)
verify(productRepository, times(1)).findByGuid(guid) verify(productRepository, times(1)).findByGuid(guid)
verify(productRepository, times(1)).save(deletedProduct) verify(productRepository, times(1)).save(deletedProductEntity)
} }
@Test @Test
fun `delete - fail - already deleted`() { fun `delete - fail - already deleted`() {
val deletedProduct = product.copy( val deletedProduct = productEntity.copy(
deletedAt = mockCurrentTime, deletedAt = mockCurrentTime,
) )
whenever(productRepository.findByGuid(any())).thenReturn(deletedProduct) whenever(productRepository.findByGuid(any())).thenReturn(deletedProduct)
@@ -124,26 +127,4 @@ class ProductServiceImplTest {
verify(productRepository, times(1)).findByGuid(guid) verify(productRepository, times(1)).findByGuid(guid)
verify(productRepository, never()).save(any()) verify(productRepository, never()).save(any())
} }
@Test
fun `syncToKafka - success`() {
whenever(productRepository.findByGuid(any())) doReturn product
productService.syncToKafka(guid, null)
verify(productRepository, times(1)).findByGuid(guid)
verify(producer, times(1)).produceProductSync(kafkaProductDto)
}
@Test
fun `syncToKafka - not found`() {
whenever(productRepository.findByGuid(any())) doReturn null
assertThrows<ProductNotFoundException> {
productService.syncToKafka(guid, null)
}
verify(productRepository, times(1)).findByGuid(guid)
verifyNoInteractions(producer)
}
} }

View File

@@ -1,19 +0,0 @@
package com.github.dannecron.demo.services.database.city
import com.github.dannecron.demo.db.entity.City
import com.github.dannecron.demo.services.database.exceptions.CityNotFoundException
import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.services.kafka.dto.CityCreateDto
import org.springframework.stereotype.Service
import java.util.*
@Service
interface CityService {
fun findByGuid(guid: UUID): City?
fun create(name: String): City
fun create(kafkaCityDto: CityCreateDto): City
@Throws(CityNotFoundException::class, AlreadyDeletedException::class)
fun delete(guid: UUID): City
}

View File

@@ -1,60 +0,0 @@
package com.github.dannecron.demo.services.database.city
import com.github.dannecron.demo.core.services.generation.CommonGenerator
import com.github.dannecron.demo.db.entity.City
import com.github.dannecron.demo.db.repository.CityRepository
import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.services.database.exceptions.CityNotFoundException
import com.github.dannecron.demo.services.kafka.dto.CityCreateDto
import org.springframework.stereotype.Service
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import java.util.UUID
@Service
class CityServiceImpl(
private val cityRepository: CityRepository,
private val commonGenerator: CommonGenerator,
): CityService {
override fun findByGuid(guid: UUID): City? = cityRepository.findByGuid(guid)
override fun create(name: String): City = City(
id = null,
guid = commonGenerator.generateUUID(),
name = name,
createdAt = commonGenerator.generateCurrentTime(),
updatedAt = null,
deletedAt = null,
).let {
cityRepository.save(it)
}
override fun create(kafkaCityDto: CityCreateDto): City = City(
id = null,
guid = UUID.fromString(kafkaCityDto.guid),
name = kafkaCityDto.name,
createdAt = OffsetDateTime.parse(kafkaCityDto.createdAt, DateTimeFormatter.ISO_OFFSET_DATE_TIME),
updatedAt = kafkaCityDto.deletedAt?.let {
OffsetDateTime.parse(it, DateTimeFormatter.ISO_OFFSET_DATE_TIME)
},
deletedAt = kafkaCityDto.deletedAt?.let {
OffsetDateTime.parse(it, DateTimeFormatter.ISO_OFFSET_DATE_TIME)
},
).let {
cityRepository.save(it)
}
override fun delete(guid: UUID): City {
val city = findByGuid(guid) ?: throw CityNotFoundException()
if (city.isDeleted()) {
throw AlreadyDeletedException()
}
return cityRepository.save(
city.copy(
deletedAt = OffsetDateTime.now(),
)
)
}
}

View File

@@ -1,25 +0,0 @@
package com.github.dannecron.demo.services.database.product
import com.github.dannecron.demo.db.entity.Product
import com.github.dannecron.demo.services.database.exceptions.AlreadyDeletedException
import com.github.dannecron.demo.services.database.exceptions.ProductNotFoundException
import com.github.dannecron.demo.services.kafka.exceptions.InvalidArgumentException
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import java.util.UUID
@Service
interface ProductService {
fun findByGuid(guid: UUID): Product?
fun findAll(pageable: Pageable): Page<Product>
fun create(name: String, price: Long, description: String?): Product
@Throws(ProductNotFoundException::class, AlreadyDeletedException::class)
fun delete(guid: UUID): Product
@Throws(ProductNotFoundException::class, InvalidArgumentException::class)
fun syncToKafka(guid: UUID, topic: String?)
}