入力値の妥当性チェック
理由: アプリケーション層 の外から送られてきた値は信用できず、不正な値が持ち込まれる可能性があるため 入力値の妥当性チェックは入力モデル(DTO)で行う イミュータブル オブジェクトにすることで、一度チェックが通れば有効な状態が維持されるようにする https://scrapbox.io/files/66908921e316bb001d293297.png
hr.icon
radish-miyazaki.icon による Go のサンプルコード SendMoneyCommand 構造体
由来は、DDD において「状態を変更する」操作である コマンド を指す code:buckpal/application/port/in/send_money_command.go
type SendMoneyCommand struct {
SourceAccountID model.AccountID
TargetAccountID model.AccountID
Money model.Money validate:"positiveMoney"
}
妥当性チェックには、github.com/go-playground/validator/v10 を用いている
インスタンスは イミュータブル にしたいが、Go はイミュータブルなインスタンスを作成できない このような場合、値レシーバ + プライベートフィールド(先頭小文字)にするが、github.com/go-playground/validator/v10 によるチェックが走らないためパブリックにしている
実現する方法があれば教えて欲しい radish-miyazaki.icon ?? money は 0 より大きい値が入るような ビジネスルール を適用したいが、model.Money という独自型なので、カスタムバリデーションが必要 code:buckpal/common/validator.go
func positiveMoney(fl factValidator.FieldLevel) bool {
money, ok := fl.Field().Interface().(model.Money)
if !ok {
return false
}
moneyInt := (*big.Int)(&money)
return moneyInt.Sign() >= 0
}
validator.RegisterValidation("positiveMoney", positiveMoney)
入力値の妥当性チェックはコンストラクタで行う
ただし、Go にはコンストラクタが存在しないので、代わりとなる NewSendMoneyCommand 関数内で行う code:buckpal/application/port/in/send_money_command.go
func NewSendMoneyCommand(
sourceAccountID model.AccountID,
targetAccountID model.AccountID,
money model.Money,
) (*SendMoneyCommand, error) {
command := &SendMoneyCommand{
money: money,
sourceAccountID: sourceAccountID,
targetAccountID: targetAccountID,
}
if err := common.Validate(command); err != nil {
return nil, err
}
return command, nil
}
依存をなるべく小さくするため、common パッケージで定義した関数を呼び出して間接的に利用
warning.icon ただし、構造体には validate タグが残るので、完全に依存は剥がせない
このように、わずかな依存を加えることで、大幅に時間や労力を節約できるのであれば、その依存を使うことが問題ないことはない