3104号室

本のレビューや音楽のことや日々思っていることを書くところ。つまり雑記。

【ドメイン駆動設計】【Clean Architecture】クリーンアーキテクチャで、いきなりドメインモデルを実装してモデルを洗練させる戦略

なんかネタ記事が続いたのでまじめなプログラミングの記事書きます。

僕はClean Architectureの考え方が好きです。

ドメイン駆動設計と相性が良く、ドメインモデルを実装に落としこむのが簡単にできます。

というのも、ドメイン層が外側に依存しないため、いきなりドメインモデルから実装を進めることができるからです。

今回はドメインモデルから初めてクリーンアーキテクチャを実装する際のイメージを書いてみたいと思います。

例はすべてGoで書いています。

ドメインモデルからいきなり作りはじめる

ドメイン駆動設計ではドメインエキスパートとの会話を通じて”ユビキタス言語”を導き出し、それを用いてドメインモデルを作成します。

例えば最初にこんなドメインモデルが導き出されたとします。

f:id:masashi-yamada0110:20180904221221p:plain

例なので、超簡単ですね。

まずは何も考えずに実装します。

package domain

// 注文(Entity)
type Order struct {
  orderId_ string // 本来はIDも値オブジェクトにするべき
}

// 注文履歴
type OrderHistory struct {
  orders_ []Order 
}

この段階では構造だけでメソッドは用意しません。

ユースケースを考えながらドメインモデルを洗練させていきます。

次にユースケースを考え実装する

ドメインエキスパートの会話で次のようなユースケースが導き出されたとします。

  • 注文履歴に含まれる注文特定の注文を表示する

超絶シンプルですがサンプルなのであしからず。

クリーンアーキテクチャの手法に則りInputPort, OutputPortを以下のように定義します。

package usecase

// DataSourceは空インターフェース
type DataSource interface{}

type InputPort interface {
  Do(...DataSource)
}

type OutputPort interface {
  Emit(...DataSource)
}

そして永続化された注文履歴を読み込むためのリポジトリのインターフェースを定義しておきます。

type OrderHistoryRepository interface {
    OrderHistory() *domain.OrderHistory
}

それらを用いてUseCaseを実装します。

type DisplayOrderHistory struct {
  outputPort_        *OutputPort
  historyRepository_ *OrderHistoryRepository
}

func NewDisplayOrderHistory(outputPort *OutputPort, historyRepository *OrderHistoryRepository) *DisplayOrderHistory() {
  return &DisplayOrderHistory{
    outputPort_: outputPort,
    historyRepository_: historyRepository,
  }
}

func(d *DeleteOrder)Do(data ...DataSource){
  if len(data) != 1 {
    // エラー処理
  }
  orderId, ok := data[0].(string)
  if !ok {
    // エラー処理
  }

  // 注文履歴から注文内容を削除する
  history := historyRepository_.OrderHistory()  // 多分本当ならユーザーIDとかキーが必要なんだろうなぁ
  order := history.Find(orderId) // どうやらFindが必要そう
  outputPort_.Emit(order)
}

おっと、実装してみるとOrderHistoryにはFind関数が必要そうですね。(わざとらしい

ということでOrderHistoryに追加しましょう。

// 注文履歴
type OrderHistory struct {
  orders_ []Order 
}
func (o *OrderHistory)Find(orderId string){
   //検索処理
}

他にも表示する内容に応じて、Orderに必要な関数も色々ありそうですね。

ドメインエキスパートに確認してみなきゃですね。

ユースケースとドメインモデルを行ったり来たりしてドメインモデルを洗練させる

こんな感じで「ドメインモデル」->「ユースケース」->「ドメインエキスパートとの対話」のループを続けることでドメインモデルを洗練させていくことができます。

ここまででデータベースの話もUIの話も使用するフレームワークの話も出てきていません。

※ UIはペーパプロトタイプを用意して合意を取っておいたほうがよいとはおもいますが。

つまり、クリーンアーキテクチャのようにドメインモデルが他から独立していれば、それらの決定を待つことなく、どんどんソフトウェアの核であるドメインモデルを作り込むことができそうです。

まとめ

UIやデータベースや使用するフレームワークが決定していなくてもいきなりドメインモデルに取り掛かってドメインエキスパートや顧客と話し合いを進められるというのはメリットだと思うので、その点でクリーンアーキテクチャは使えるのではないかという話でした。

Clean Architecture 達人に学ぶソフトウェアの構造と設計

Clean Architecture 達人に学ぶソフトウェアの構造と設計