Lift DomainMessage subtypes to top-level declarations in example
This commit is contained in:
parent
687c4c9d7f
commit
9d7c5f07f7
@ -1,11 +1,31 @@
|
|||||||
package com.github.michaelbull.result.example
|
package com.github.michaelbull.result.example
|
||||||
|
|
||||||
import com.github.michaelbull.result.*
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import com.github.michaelbull.result.andThen
|
||||||
import com.github.michaelbull.result.example.model.domain.Customer
|
import com.github.michaelbull.result.example.model.domain.Customer
|
||||||
|
import com.github.michaelbull.result.example.model.domain.CustomerCreated
|
||||||
import com.github.michaelbull.result.example.model.domain.CustomerId
|
import com.github.michaelbull.result.example.model.domain.CustomerId
|
||||||
|
import com.github.michaelbull.result.example.model.domain.CustomerIdMustBePositive
|
||||||
|
import com.github.michaelbull.result.example.model.domain.CustomerNotFound
|
||||||
|
import com.github.michaelbull.result.example.model.domain.CustomerRequired
|
||||||
|
import com.github.michaelbull.result.example.model.domain.DatabaseError
|
||||||
|
import com.github.michaelbull.result.example.model.domain.DatabaseTimeout
|
||||||
import com.github.michaelbull.result.example.model.domain.DomainMessage
|
import com.github.michaelbull.result.example.model.domain.DomainMessage
|
||||||
|
import com.github.michaelbull.result.example.model.domain.EmailAddressChanged
|
||||||
|
import com.github.michaelbull.result.example.model.domain.EmailInvalid
|
||||||
|
import com.github.michaelbull.result.example.model.domain.EmailRequired
|
||||||
|
import com.github.michaelbull.result.example.model.domain.EmailTooLong
|
||||||
|
import com.github.michaelbull.result.example.model.domain.FirstNameRequired
|
||||||
|
import com.github.michaelbull.result.example.model.domain.FirstNameTooLong
|
||||||
|
import com.github.michaelbull.result.example.model.domain.LastNameRequired
|
||||||
|
import com.github.michaelbull.result.example.model.domain.LastNameTooLong
|
||||||
|
import com.github.michaelbull.result.example.model.domain.SqlCustomerInvalid
|
||||||
import com.github.michaelbull.result.example.model.dto.CustomerDto
|
import com.github.michaelbull.result.example.model.dto.CustomerDto
|
||||||
import com.github.michaelbull.result.example.service.CustomerService
|
import com.github.michaelbull.result.example.service.CustomerService
|
||||||
|
import com.github.michaelbull.result.mapBoth
|
||||||
|
import com.github.michaelbull.result.mapError
|
||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.application.install
|
import io.ktor.application.install
|
||||||
@ -72,36 +92,36 @@ fun Application.main() {
|
|||||||
|
|
||||||
private fun readId(values: ValuesMap): Result<Long, DomainMessage> {
|
private fun readId(values: ValuesMap): Result<Long, DomainMessage> {
|
||||||
val id = values["id"]?.toLongOrNull()
|
val id = values["id"]?.toLongOrNull()
|
||||||
return if (id != null) Ok(id) else Err(DomainMessage.CustomerRequired)
|
return if (id != null) Ok(id) else Err(CustomerRequired)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun messageToResponse(message: DomainMessage) = when (message) {
|
private fun messageToResponse(message: DomainMessage) = when (message) {
|
||||||
DomainMessage.CustomerRequired,
|
CustomerRequired,
|
||||||
DomainMessage.CustomerIdMustBePositive,
|
CustomerIdMustBePositive,
|
||||||
DomainMessage.FirstNameRequired,
|
FirstNameRequired,
|
||||||
DomainMessage.FirstNameTooLong,
|
FirstNameTooLong,
|
||||||
DomainMessage.LastNameRequired,
|
LastNameRequired,
|
||||||
DomainMessage.LastNameTooLong,
|
LastNameTooLong,
|
||||||
DomainMessage.EmailRequired,
|
EmailRequired,
|
||||||
DomainMessage.EmailTooLong,
|
EmailTooLong,
|
||||||
DomainMessage.EmailInvalid ->
|
EmailInvalid ->
|
||||||
Pair(HttpStatusCode.BadRequest, "There is an error in your request")
|
Pair(HttpStatusCode.BadRequest, "There is an error in your request")
|
||||||
|
|
||||||
// events
|
// events
|
||||||
DomainMessage.CustomerCreated ->
|
CustomerCreated ->
|
||||||
Pair(HttpStatusCode.Created, "Customer created")
|
Pair(HttpStatusCode.Created, "Customer created")
|
||||||
|
|
||||||
is DomainMessage.EmailAddressChanged ->
|
is EmailAddressChanged ->
|
||||||
Pair(HttpStatusCode.OK, "Email address changed from ${message.old} to ${message.new}")
|
Pair(HttpStatusCode.OK, "Email address changed from ${message.old} to ${message.new}")
|
||||||
|
|
||||||
// exposed errors
|
// exposed errors
|
||||||
DomainMessage.CustomerNotFound ->
|
CustomerNotFound ->
|
||||||
Pair(HttpStatusCode.NotFound, "Unknown customer")
|
Pair(HttpStatusCode.NotFound, "Unknown customer")
|
||||||
|
|
||||||
// internal errors
|
// internal errors
|
||||||
DomainMessage.SqlCustomerInvalid,
|
SqlCustomerInvalid,
|
||||||
DomainMessage.DatabaseTimeout,
|
DatabaseTimeout,
|
||||||
is DomainMessage.DatabaseError ->
|
is DatabaseError ->
|
||||||
Pair(HttpStatusCode.InternalServerError, "Internal server error occurred")
|
Pair(HttpStatusCode.InternalServerError, "Internal server error occurred")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ import com.github.michaelbull.result.Ok
|
|||||||
data class CustomerId(val id: Long) {
|
data class CustomerId(val id: Long) {
|
||||||
companion object {
|
companion object {
|
||||||
fun create(id: Long?) = when {
|
fun create(id: Long?) = when {
|
||||||
id == null -> Err(DomainMessage.CustomerRequired)
|
id == null -> Err(CustomerRequired)
|
||||||
id < 1 -> Err(DomainMessage.CustomerIdMustBePositive)
|
id < 1 -> Err(CustomerIdMustBePositive)
|
||||||
else -> Ok(CustomerId(id))
|
else -> Ok(CustomerId(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,35 +3,34 @@ package com.github.michaelbull.result.example.model.domain
|
|||||||
/**
|
/**
|
||||||
* All possible things that can happen in the use-cases
|
* All possible things that can happen in the use-cases
|
||||||
*/
|
*/
|
||||||
sealed class DomainMessage {
|
sealed class DomainMessage
|
||||||
|
|
||||||
/* validation errors */
|
/* validation errors */
|
||||||
|
|
||||||
object CustomerRequired : DomainMessage()
|
object CustomerRequired : DomainMessage()
|
||||||
object CustomerIdMustBePositive : DomainMessage()
|
object CustomerIdMustBePositive : DomainMessage()
|
||||||
|
|
||||||
object FirstNameRequired : DomainMessage()
|
object FirstNameRequired : DomainMessage()
|
||||||
object FirstNameTooLong : DomainMessage()
|
object FirstNameTooLong : DomainMessage()
|
||||||
|
|
||||||
object LastNameRequired : DomainMessage()
|
object LastNameRequired : DomainMessage()
|
||||||
object LastNameTooLong : DomainMessage()
|
object LastNameTooLong : DomainMessage()
|
||||||
|
|
||||||
object EmailRequired : DomainMessage()
|
object EmailRequired : DomainMessage()
|
||||||
object EmailTooLong : DomainMessage()
|
object EmailTooLong : DomainMessage()
|
||||||
object EmailInvalid : DomainMessage()
|
object EmailInvalid : DomainMessage()
|
||||||
|
|
||||||
/* events */
|
/* events */
|
||||||
|
|
||||||
object CustomerCreated : DomainMessage()
|
object CustomerCreated : DomainMessage()
|
||||||
class EmailAddressChanged(val old: String, val new: String) : DomainMessage()
|
class EmailAddressChanged(val old: String, val new: String) : DomainMessage()
|
||||||
|
|
||||||
/* exposed errors */
|
/* exposed errors */
|
||||||
|
|
||||||
object CustomerNotFound : DomainMessage()
|
object CustomerNotFound : DomainMessage()
|
||||||
|
|
||||||
/* internal errors */
|
/* internal errors */
|
||||||
|
|
||||||
object SqlCustomerInvalid : DomainMessage()
|
object SqlCustomerInvalid : DomainMessage()
|
||||||
object DatabaseTimeout : DomainMessage()
|
object DatabaseTimeout : DomainMessage()
|
||||||
class DatabaseError(val reason: String?) : DomainMessage()
|
class DatabaseError(val reason: String?) : DomainMessage()
|
||||||
}
|
|
||||||
|
@ -10,9 +10,9 @@ data class EmailAddress(
|
|||||||
private val pattern = ".+@.+\\..+".toRegex() // crude validation
|
private val pattern = ".+@.+\\..+".toRegex() // crude validation
|
||||||
|
|
||||||
fun create(address: String?) = when {
|
fun create(address: String?) = when {
|
||||||
address == null || address.isBlank() -> Err(DomainMessage.EmailRequired)
|
address == null || address.isBlank() -> Err(EmailRequired)
|
||||||
address.length > 20 -> Err(DomainMessage.EmailTooLong)
|
address.length > 20 -> Err(EmailTooLong)
|
||||||
!address.matches(pattern) -> Err(DomainMessage.EmailInvalid)
|
!address.matches(pattern) -> Err(EmailInvalid)
|
||||||
else -> Ok(EmailAddress(address))
|
else -> Ok(EmailAddress(address))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@ data class PersonalName(
|
|||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun create(first: String?, last: String?) = when {
|
fun create(first: String?, last: String?) = when {
|
||||||
first == null || first.isBlank() -> Err(DomainMessage.FirstNameRequired)
|
first == null || first.isBlank() -> Err(FirstNameRequired)
|
||||||
last == null || last.isBlank() -> Err(DomainMessage.LastNameRequired)
|
last == null || last.isBlank() -> Err(LastNameRequired)
|
||||||
first.length > 10 -> Err(DomainMessage.FirstNameTooLong)
|
first.length > 10 -> Err(FirstNameTooLong)
|
||||||
last.length > 10 -> Err(DomainMessage.LastNameTooLong)
|
last.length > 10 -> Err(LastNameTooLong)
|
||||||
else -> Ok(PersonalName(first, last))
|
else -> Ok(PersonalName(first, last))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,13 @@ import com.github.michaelbull.result.Ok
|
|||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
import com.github.michaelbull.result.andThen
|
import com.github.michaelbull.result.andThen
|
||||||
import com.github.michaelbull.result.example.model.domain.Customer
|
import com.github.michaelbull.result.example.model.domain.Customer
|
||||||
|
import com.github.michaelbull.result.example.model.domain.CustomerCreated
|
||||||
import com.github.michaelbull.result.example.model.domain.CustomerId
|
import com.github.michaelbull.result.example.model.domain.CustomerId
|
||||||
|
import com.github.michaelbull.result.example.model.domain.CustomerNotFound
|
||||||
|
import com.github.michaelbull.result.example.model.domain.DatabaseError
|
||||||
|
import com.github.michaelbull.result.example.model.domain.DatabaseTimeout
|
||||||
import com.github.michaelbull.result.example.model.domain.DomainMessage
|
import com.github.michaelbull.result.example.model.domain.DomainMessage
|
||||||
|
import com.github.michaelbull.result.example.model.domain.EmailAddressChanged
|
||||||
import com.github.michaelbull.result.example.model.entity.CustomerEntity
|
import com.github.michaelbull.result.example.model.entity.CustomerEntity
|
||||||
import com.github.michaelbull.result.map
|
import com.github.michaelbull.result.map
|
||||||
import com.github.michaelbull.result.mapBoth
|
import com.github.michaelbull.result.mapBoth
|
||||||
@ -49,24 +54,24 @@ object CustomerService {
|
|||||||
|
|
||||||
private fun createCustomer(entity: CustomerEntity) =
|
private fun createCustomer(entity: CustomerEntity) =
|
||||||
Result.of { repository.insert(entity) }
|
Result.of { repository.insert(entity) }
|
||||||
.map { DomainMessage.CustomerCreated }
|
.map { CustomerCreated }
|
||||||
.mapError(::exceptionToDomainMessage)
|
.mapError(::exceptionToDomainMessage)
|
||||||
|
|
||||||
private fun findCustomer(customers: Collection<Customer>, id: CustomerId): Result<Customer, DomainMessage.CustomerNotFound> {
|
private fun findCustomer(customers: Collection<Customer>, id: CustomerId): Result<Customer, CustomerNotFound> {
|
||||||
val customer = customers.find { it.id == id }
|
val customer = customers.find { it.id == id }
|
||||||
return if (customer != null) Ok(customer) else Err(DomainMessage.CustomerNotFound)
|
return if (customer != null) Ok(customer) else Err(CustomerNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun differenceBetween(old: Customer, new: Customer): DomainMessage.EmailAddressChanged? {
|
private fun differenceBetween(old: Customer, new: Customer): EmailAddressChanged? {
|
||||||
return if (new.email != old.email) {
|
return if (new.email != old.email) {
|
||||||
DomainMessage.EmailAddressChanged(old.email.address, new.email.address)
|
EmailAddressChanged(old.email.address, new.email.address)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exceptionToDomainMessage(t: Throwable) = when (t) {
|
private fun exceptionToDomainMessage(t: Throwable) = when (t) {
|
||||||
is SQLTimeoutException -> DomainMessage.DatabaseTimeout
|
is SQLTimeoutException -> DatabaseTimeout
|
||||||
else -> DomainMessage.DatabaseError(t.message)
|
else -> DatabaseError(t.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.github.michaelbull.result.example.service
|
package com.github.michaelbull.result.example.service
|
||||||
|
|
||||||
import com.github.michaelbull.result.example.model.entity.CustomerEntity
|
|
||||||
import com.github.michaelbull.result.example.model.domain.repository.CustomerRepository
|
import com.github.michaelbull.result.example.model.domain.repository.CustomerRepository
|
||||||
|
import com.github.michaelbull.result.example.model.entity.CustomerEntity
|
||||||
import java.sql.SQLException
|
import java.sql.SQLException
|
||||||
import java.sql.SQLTimeoutException
|
import java.sql.SQLTimeoutException
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user