⛳
Go言語における契約プログラミングと有効状態の保証
Go言語における契約プログラミングと有効状態の保証
ゼロ値とドメインモデル
Go言語は「ゼロ値」を持つことで、型の初期化をしないでもコンパイルしますが、それはときに無効なドメインオブジェクトを作ります。たとえば UserID("")
や Order{}
はコンパイルするものの、ビジネスルール上は無効な状態です。
契約プログラミングの手段
Validate() error
メソッド
1. ドメインオブジェクトには、メソッド Validate() error
を実装させ、有効状態を保証します。これを通して、各型は自分の有効性を自分で検証できるようにします。
type Validator interface {
Validate() error
}
2. コンストラクタ内での検証
型の初期化はコンストラクタの関数 (NewXxx)に限定し、その中で必要な前提条件を検証します:
func NewUserID(id string) (UserID, error) {
if len(id) == 0 {
return "", fmt.Errorf("UserID must not be empty")
}
return UserID(id), nil
}
後続部でも Validate()
を呼び出せば、未知な入力やデータの加工に対応できます。
3. AssertValidatable への落とし込み
既定の合成ファイルでは AssertValidatable(v)
のようなヘルパー関数を用意し、nil検査+検証をまとめて行います。
func AssertValidatable(v Validator) {
if v == nil {
panic("Validator is nil")
}
if err := v.Validate(); err != nil {
panic(err)
}
}
これは「プログラマーのミスは明確にパニックで示せ」という fail-fast 思想に基づいたものです。
4. DTO/外部インプットは error 戻し
ライブラリやサービス内では panic は推奨されないため、ユーザー入力などが原因の障害は error
戻しにします。
おわりに
Go言語では「型は作成された時点で有効であるべき」という思想が重要です。これを実現するために、Validateメソッドやコンストラクタでの検証を用いて、未検証でのインスタンス作成を防ぐことが推奨されます。
無効状態のオブジェクトをなるべく早期に検出し、平文時に修正を可能にするという思想は、Goのドメインデザインにおいて彼正しいプラクティスとされています。
Discussion