Lean Architecture APPENDIX A, ScalaでのDCIサンプルです。コプリエンさん作です。
increaseBalance
でUnit
を返すとはないわ〜。(今どき可変オブジェクトを中心にドメインを考えたくないという意味で。多分こういう考え方の人は関数型勢に多い。
import java.time.Instant // 元のサンプルはjava.util.Dateだったがさすがにそれは辞めろ的な意味で書き換えた
// Data
trait Account {
protected var balance: Long = 0
def availableBalance: Long = balance
// Unitを返すのは避けたい
def decreaseBalance(amount: Long): Unit = {
require(amount >= 0)
balance -= amount
}
// Unitを返すのは避けたい
def increaseBalance(amount: Long): Unit =
balance += amount
// ↓ こういうのもドメインの外側でやるべきでしょう
def updateLong(msg: String, date: Instant, amount: Long): Unit =
println(s"Account $toString, $msg, $date, $amount")
}
// Methodless Role
trait MoneySink {
def increaseBalance(amount: Long): Unit
def updateLong(msg: String, date: Instant, amount: Long): Unit
}
trait MoneySource {
def transferTo(amount: Long, recipient: MoneySink): Unit
}
// Methodfull Role
trait TransferMoneySink extends MoneySink { this: Account =>
def transferFrom(amount: Long, src: MoneySource): Unit = {
increaseBalance(amount)
updateLong("Transfer in", Instant.now(), amount)
}
}
trait TransferMoneySource extends MoneySource {
this: Account =>
override def transferTo(amount: Long, recipient: MoneySink): Unit = {
decreaseBalance(amount)
recipient.increaseBalance(amount)
updateLong("Transfer out", Instant.now(), amount)
recipient.updateLong("Transfer In", Instant.now(), amount)
}
}
class SavingsAccount extends Account {
override def toString: String = "Savings"
}
class CheckingAccount extends Account {
override def toString: String = "Check"
}
// Context
object Main extends App {
val source = new SavingsAccount with TransferMoneySource
val sink = new CheckingAccount with TransferMoneySink
source.increaseBalance(100000)
source.transferTo(200, sink)
println(source.availableBalance + ", " + sink.availableBalance)
}
さて、こう書けるようにするにはどうしたらよいのだ…。
object Main extends App {
val source = new SavingsAccount with TransferMoneySource
val sink = new CheckingAccount with TransferMoneySink
val newSource1: Account with MoneySource = source.increaseBalance(100000)
val result: (Account with MoneySource, Account with MoneySink) = newSource1.transferTo(200, sink)
println(result._1.availableBalance + ", " + result._2.availableBalance)
}
DCIがわからんという人は以下参照してください。
https://digitalsoul.hatenadiary.org/entry/20100131/1264925022