ドメインモデルの実装
「自身の口座から別の口座に送金する」ユースケース
口座を表すクラス Account を作成する
上記のクラスに以下の 2 つのメソッドを定義する
withdraw: 指定した金額を引き出す
deposit: 指定した金額を預け入れる
hr.icon
radish-miyazaki.icon による Go のサンプルコード code:buckpal/application/domain/model/account.go
type Account struct {
id *AccountID
baselineBalance Money
activityWindow ActivityWindow
}
詳細
Account: 口座を表現するもの
id: このオブジェクトを一意にする値
AccountID は DDD における 値オブジェクト に該当 radish-miyazaki.icon baselineBalance: 基準日が始まる時点での口座残高
activityWindow: 基準日以降の口座履歴
基準日
すべての取引をオンメモリに保存するとリソース不足に陥るため、基準日以降の取引を持つようにする
そして、baselineBalance の金額に対して、基準日以降に発生したすべての取引(activityWindow)の金額を反映させることで、現時点の口座残高を算出できるようにしている
ActivityWindow: 取引履歴を表現するもの
内部的には Activity のスライスで表現している
Activity : 口座に対して行われる、引き出し・預け入れに関するすべての取引を表現するもの
Account と ActivityWindow、Activity は DDD における エンティティ に該当 radish-miyazaki.icon code:buckpal/application/domain/model/account.go
func (a *Account) Withdraw(money Money, targetAccountID AccountID) bool {
if !a.mayWithdraw(money) {
return false
}
withdrawal := NewActivity(
*a.id,
*a.id,
targetAccountID,
time.Now(),
money,
)
a.activityWindow.AddActivity(*withdrawal)
return true
}
func (a *Account) mayWithdraw(money Money) bool {
return AddMoney(a.CalculateBalance(), money.Negate()).IsPositiveOrZero()
}
func (a *Account) Deposit(money Money, sourceAccountID AccountID) bool {
deposit := NewActivity(
*a.id,
sourceAccountID,
*a.id,
time.Now(),
money,
)
a.activityWindow.AddActivity(*deposit)
return true
}
詳細
Deposit メソッド: 預け入れの取引を activityWindow フィールドに追加する
Withdraw メソッド: 引き出しの取引をactivityWindow フィールドに追加する
このとき、「送金元の口座は自身の口座残高を超えた金額を送金してはいけない」という ビジネスルール をチェックする