Zenn
👤

"WriteModel をクエリ時に使う"と"ReadModel をつくる"との折衷案

に公開1

前提

以下のようなWriteModelがあるとする。

type Product struct{
	NetPrice int64
	Cost int64
}

Computed な以下のような値がある(丸め誤差などは一旦考慮しない)

func (p *Product) SalesPrice() float64 {
	return (p.NetPrice + p.Cost) * 0.8
}

この SalesPrice メソッドは WriteModel でも利用するため以下のような選択肢が生まれる

一番楽: WriteModel をクエリ時にも使う

WriteModel を用いれば特段新しい実装をせずにクエリできる。
しかし以下の点が引っかかる。

  • ORMの構造体 → ドメインモデル → DTO の変換が必要になる。ORMの構造体 → DTO にしたい
  • WriteModel にはクエリに不要なメソッドまで含まれており用途がわかりにくい

一番リッチ: ReadModelをつくる

type ProductReac struct{
	NetPrice int64
	Cost int64
}

func (p *ProductRead) SalesPrice() float64 {
	return (p.NetPrice + p.Cost) * 0.8
}

今回の提案: WriteModel が関数化し、クエリ時には関数単体で使う

type Product struct{
	NetPrice int64
	Cost int64
}

func (p *Product) SalesPrice() float64 {
	return CalculateSalesPrice(p.NetPrice, p.Cost)
}

// [ココ] ドメインロジックを関数にカプセル化する
func CalculateSalesPrice(netPrice, cost int64) float64 {
	return (netPrice + cost) * 0.8
}

CalculateSalesPrice 関数をクエリ時にも使う。
これにより "ReadModelをつくる" ほどの記述をせず、 "WriteModelをクエリ時にも使う" ほど用途外に利用することもなく済む。

Discussion

kaito2kaito2

このほうがテスタビリティも上がる気がしている(なんとなく)

ログインするとコメントできます