О чем речь

https://www.47deg.com/blog/functional-domain-modeling/

Пример предметной области

data class Event(
  val id: Long
  val title: String,
  val organizer: String,
  val description: String,
  val date: LocalDate
)

Event(
  0L,
  "Simon Vergauwen",
  "In this blogpost we dive into functional DDD...",
  "Functional Domain Modeling",
  LocalDate.now()
)

Повышаем безопасность

inline class EventId(val value: Long)
inline class Organizer(val value: String)
inline class Title(val value: String)
inline class Description(val value: String)
data class Event(val id: EventId
  val title: Title,
  val organizer: Organizer,
  val description: Description,
  val date: LocalDate)

Event(EventId(0L),
  Organizer("Simon Vergauwen"),
  Description("In this blogpost we dive into functional DDD..."),
  Title("Functional Domain Modeling"),
  LocalDate.now())

Произведение и суммирование типов

enum class AgeRestriction(val description: String) {
  General("All ages admitted. Nothing that would offend parents for viewing by children."),
  PG("Some material may not be suitable for children. Parents urged to give \"parental guidance\""),
  PG13("Some material may be inappropriate for children under 13. Parents are urged to be cautious."),
  Restricted("Under 17 requires accompanying parent or adult guardian. Contains some adult material."),
  NC17("No One 17 and Under Admitted. Clearly adult.")
}

Опциональные поля

inline class Url(val value: String)

inline class City(val value: String)
inline class Street(val value: String)
data class Address(val city: City, val street: Street)

data class Event(
  val id: EventId
  val title: Title,
    ...
  val isOnline: Boolean,
  val url: Url?,
  val address: Address?
)

Проблема опциональных полей

fun printLocation(event: Event): Unit =
  if(event.isOnline) {
    event.url?.value?.let(::println)
  } else {
    event.address?.let(::println)
  }

Event(
	Id(0L),
	Title("Functional Domain Modeling"),
    ...	
    true,
	null,
	null
)

Описание опциональных полей

sealed class Event {  abstract val id: EventId
  abstract val title: Title
    ...
  data class Online(override val id: EventId,
    override val title: Title,
    ...
    val url: Url
  ) : Event()
  data class AtAddress(override val id: EventId,
    override val title: Title,
    ...
    val address: Address
  ) : Event() }

Использование

fun printLocation(event: Event): Unit =
  when(event) {
  	is Online -> println(event.url.value)
  	is AtAddress -> println(
        "${event.address.city}: ${event.address.street}")
  }

Предметная область в проекте

@Serializable
class Student(
    val firstname: Firstname,
    val surname: Surname
){
    fun fullname() =
        "$firstname $surname"
}

Value классы

@Serializable
@JvmInline
value class Firstname(val name: String) {
    override fun toString() = name
}

Изменения в проекте

val CStudentAdd = 
...
props.saveElement(
    Student(Firstname(firstname), Surname(surname))
)