背景
業務で必要になったため、Goを本格的に学んでいくことにした。 Go言語、言語以外のことも踏まえて学んだことを整理するために記載していくことにする。
DDDとフォルダ構成
初めてちゃんとDDDっぽくフォルダを構成しているプロジェクトに出会ったので忘れないようにメモっておく。
domain/
X/
|- model.go
|- repository.go # interfaceの定義のみ
|- e.t.c
infrastructure/
- x_repository.go
usecase/
- x_usecase.go
今まで少し勘違いしていた/間違っていた部分。
とりあえずRepository層
間違ってやっていたこと: repository.goに実装だけを記載する。IFを定義しない。
問題: 業務ロジックと外部サービス依存を疎結合にできてなかった。とりあえずリポジトリ層をおくだけになっていた。
業務ロジックとrepositoryレイヤの違い
勘違いしていたこと: 結局、Repository層にビジネスロジック書くんでしょ?ドメインはテーブルのマッピングだけ。
今の解釈: そうせざるを得ない状況は現実的にある。ただ、永続化レイヤからデータを取得した後のドメインへの操作(価格の割引など)は、できる限りドメインに対して実施する。 その変更後のドメインをリポジトリに渡すようにすると、ドメインとリポジトリ層を分離しやすい。
契約ベースの実装とテスト
これもGoに限らない話だが、ユニットテストをする際にどう手をつけていくかの話。 個人的にしっくりきているのが、
- 契約を明らかにする。事前条件、事後条件、不変条件の三つがよく使われる。事前条件は入力値が満たすべき条件、事後条件は出力が満たすべき条件、不変条件はそのデータそのものが常に満たすべき条件(例: 銀行残高は常に0以上)。
- 契約ごとにテストのメソッドを決める。
- 実装していく。
全てのプログラムは入力、変換、出力の3構成とみなすことができる。 入力が正しいかどうかは、条件を満たさない値を渡してエラーが発生することを確認する。 変換が正しいかどうかは出力、そして不変条件を検証することで確認する。
interface -> 具体Typeへの変換
msg # &Message interfaceを満たすストラクトのポインタ
VoteResponse # 具体の型
got, ok := msg.(*MessageA) # ポインタだから、*をつける
Goの型アサーションという仕組みみたい。ある関数の戻り値がinterfaceとなっている時、コンパイラはその戻り値がどの具体の値かをわからない。 従って、型アサーションを使ってこの戻り値の中身が*MessageA (MessageAへのポインタ、上の例)かを明示的にチェックする必要がある。