有一陣子沒有更新了,最近有一些心得來補充一下。
為什麼要使用 Value Class,有寫在另一篇 https://ithelp.ithome.com.tw/m/articles/10292720 。主要就是原始型別,有機會塞錯,所以用 value class 確保,並且達到 Domain Design。所以我們把原始型別都建立一個 inline value type, 並且也把 mongodb 的 key 用 get 寫在這。
https://github.com/hmchangm/getting-start-QK/blob/master/src/main/kotlin/tw/brandy/ironman/entity/Film.kt
@JvmInline
value class Title(val raw: String) {
companion object {
val key get() = "title"
}
}
@JvmInline
value class Director(val raw: String) {
companion object {
val key get() = "director"
}
}
UUID 的處理
@JvmInline
value class EpisodeId(private val s: UUID) {
val raw get() = s.toString()
companion object {
fun from(str: String): Either<WrongUUIDFormat, EpisodeId> {
return Either.catch {
EpisodeId(UUID.fromString(str))
}.mapLeft {
WrongUUIDFormat(str)
}
}
val key get() = "episodeId"
}
}
DateTime Wrapper
@JvmInline
value class ReleaseDate(val raw: Date) {
companion object {
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
val key get() = "releaseDay"
fun fromIsoDate(iso: String): ReleaseDate {
return ReleaseDate(formatter.parse(iso))
}
}
}
Data Class with inline fields
data class Film(
val episodeId: EpisodeId,
val title: Title,
val director: Director,
val releaseDate: ReleaseDate
)
在 DAO 作 collection type transformation
val from: suspend (Document) -> Either<AppError, Film> = { doc ->
either {
Film(
doc.getString(EpisodeId.key).let { EpisodeId.from(it) }.bind(),
Title(doc.getString(Title.key)),
Director(doc.getString(Director.key)),
ReleaseDate(doc.getDate(ReleaseDate.key))
)
}
}
val to: suspend (Film) -> Document = { film ->
Document().apply {
append(EpisodeId.key, film.episodeId.raw)
append(Title.key, film.title.raw)
append(Director.key, film.director.raw)
append(ReleaseDate.key, film.releaseDate.raw)
}
}