LoginSignup
6
3

More than 3 years have passed since last update.

【DDD】ドメイン駆動設計ってなんじゃらほい

Last updated at Posted at 2020-06-28

ドメイン駆動設計についての読書記録です。
主に下記書籍を参考にしています。
ドメイン駆動設計入門 ボトムアップでわかる! ドメイン駆動設計の基本 | 成瀬 允宣 |本 | 通販 | Amazon
ドメイン駆動設計 モデリング/実装ガイド - little-hands - BOOTH

用語の整理

まずはDDDに出てくる用語の整理をしていきます。

ドメイン

プログラムを適応する対象となる領域のことを指します。

利用者に焦点を当てる

利用者が何に課題を感じていて、何を解決したいのかを考える必要があります。
この2つを理解してソフトウェアに反映していきます。
これが大原則です。

モデル

現実の事業もしくは概念を抽象化したものです。
モデリングは、このモデルを得るまでの過程を指しています。

ドメインオブジェクト

上であげたモデルをソフトウェア上で動作するようにしたものです。
image.png

値オブジェクト(ValueObject)

性質

  • 不変
  • 交換可能
  • 等価性による比較

エンティティ

データベースのエンティティとは異なる概念です。

性質

  • 可変
  • 同じ属性でも区別
  • 同一性により区別

値オブジェクトとの違い

どこまでを値オブジェクトにして、どこまでをエンティティにするかは非常に難しいです。
その一つの解決策として、 ライフサイクルがあります。
対象が仮に「ユーザー」だった場合、アプリケーションで会員登録したり、ログインしたり、退会したりとアプリケーションの中でライフサイクルが存在する場合は、エンティティになります。
なので、可変か不変かで判断できそうです。
ただしこの判断は何を題材するかによって大きく変わるのでサービスによってしっかり考えるようにします。

ドメインサービス

オブジェクトに持たせる振る舞いとして正しくない場合にドメインサービスを使用します。
メソッドを実装した場合、オブジェクトのメソッドを日本語にした場合に日本がおかしい場合は、ドメインサービスに持たせれば良い。
ただ気をつけなければいけないのは、なんでもかんでもドメインサービスにいれてはいけないということです。

リポジトリ

ドメインオブジェクトの永続化や再構築を行います。
集約ごとに一つのリポジトリが存在し、

リポジトリを使用する理由

ソフトウェアに柔軟性を持たせるのが一番だと思います。
さらに具体的にいうと、テストが容易に実行できるようになります。

アーキテクチャ

ビジネスロジックが正しい場所にかき続けられることを助けるのがアーキテクチャです。
アーキテクチャはあくまで方針であることを忘れてはいけません。

集約

必ず守りたい強い整合性を持ったオブジェクトのまとまりのことをさします。

境界

集約する場合、必ず付いて回ります。

依存関係のコントロール

具象が抽象に依存するようにしなければいけません。
そうすることで変更に強いソフトウェアを作りことはsが可能になります。

依存関係逆転の法則(Dependency Inversion Principle)

抽象に依存させることが推奨されています。
より具体的に言うと、ドメイン層で定義したインターフェースに依存することをさします。
依存関係逆転の原則の重要性について - Eureka Engineering - Medium
1分でわかる依存関係逆転の原則(DIP) - Qiita

責務

このクラスが何をするクラスなのかを示します。

設計

DDDから実際の設計に落とすのは下記が非常に参考になりました。
「実践ドメイン駆動設計」を読んだので、実際にDDDで設計して作ってみた! - Qiita

コンテキストモデル
要求モデル(要望の洗い出し) → プロダクトマネージャーからビジネスで実現したいことヒアリング、洗い出し
ドメインモデル
ユースケースモデリング(ユースケース図)
画面設計(画面遷移図)
画面設計(ワイヤーフレーム)
ユースケースモデリング(ユースケース記述)
ロバストネス分析
クラス図
データモデリング(ER図)
UI設計
実装に入る...
「実践ドメイン駆動設計」を読んだので、実際にDDDで設計して作ってみた! - Qiita より引用

徐々にやっていこう。。。。

ユースケース

ユーザーストーリーを作成し、それに基づいたユースケースを洗い出します。
ここの洗い出しに関しては、下記書籍を参考に掘り下げていきたいと思っています。
ユースケース駆動開発実践ガイド (OOP Foundations)
良いユースケースを書くための発想法
ユースケースをモデリングする - Qiita
若手エンジニア必読!超絶分かるユースケース図-全知識と書き方5ステップ

Goでの実装

アーキテクチャ

アーキテクチャは、レイヤードアーキテクチャを想定します。
image.png

Goで,レイヤードアーキテクチャ - Qiita
ドメイン駆動設計で実装を始めるのに一番とっつきやすいアーキテクチャは何か[DDD] - little hands' lab
【GO/DDD】レイヤードアーキテクチャの整理 - Qiita

全体図のフォルダ構成は下記の通りです。

├── app
│   ├── application
│   │   └── usecase
│   │       ├── survey.go
│   ├── domain
│   │   ├── model
│   │   │   ├── survey.go
│   │   ├── repository
│   │   │   ├── survey.go
│   │   └── service
│   │       ├── survey.go
│   ├── infrastructure
│   │   ├── database.go
│   │   └── persistence
│   │       ├── survey.go
│   └── presentation
│       ├── handler
│       │   ├── survey.go
│       ├── middleware
│       │   └── middleware.go
│       └── router.go
├── db

ファイルの分割

集約の境界については、ファイルが境界になっています。
また、値オブジェクトやエンティティは domain層で表現されることになります。

application

ドメインロジックを呼び出し、その結果をただ返すことに特化します。
基本薄い層になります。

domain

model, repository, serviceで構成されます。

model

使用するデータ構造とドメインロジックを書きます。

// データ構造の部分
package model

type Survey struct {
    SurveyID         uint           `json:"survey_id" db:"survey_id"`
    Question         string         `json:"question" db:"question"`
    SurveyAnswerType string         `json:"survey_answer_type" db:"survey_answer_type"`
}

// ドメインロジック
....

repository

ここが依存させるべき抽象の箇所です。
依存関係逆転の法則では、抽象に依存させることが推奨されていました。
理由は、上位モジュールが下位モジュールに依存しません。
つまりこれは、インターフェースを満たすモジュールであればどんなものでも使用できることを指しましす。
上位モジュールが使いたい下位モジュールを選択することができるのです。

package repository

import "github.com/app/domain/model"

type SurveyAccessor interface {
    ListSurveyByCampaignID(campaignID uint) (*[]model.Survey, error)
}

service

ドメインサービスに書くべき実装を書きます。

package service

import (
    "github.com/app/domain/model"
    "github.com/app/domain/repository"
)

// repository経由でアクセスするためのAccessor作成
type SurveyService struct {
    serviceAccesor repository.SurveyAccessor
}

func NewSurveyService(sa repository.SurveyAccessor) *SurveyService {
    return &SurveyService{
        serviceAccesor: sa,
    }
}

func (ss *SurveyService) ListSurveyByCampaignID(campaignID uint) (*[]model.Survey, error) {
    surveys, err := ss.serviceAccesor.ListSurveyByCampaignID(campaignID)
    if err != nil {
        return nil, err
    }
    return surveys, nil
}

infrastracture

データベース(データストア)関連の処理(SQL)などを書きます。

presentation

http などの入出力のプロトコルを定義します。

その他気になったところ

Go言語での実装

Goのpackage構成と開発のベタープラクティス - Tech Blog - Recruit Lifestyle Engineer
Go言語でClean Architectureを実現して、gomockでテストしてみた - Qiita
Goにおけるドメインオブジェクト設計の考察 - 日記マン
【Go+DDD】エンティティと値オブジェクトの実装方法(自己流) - yyh-gl's Tech Blog

ドメインモデルからデータベースの設計をする順番

DDDのモデリングとは何なのか、 そしてどうコードに落とすのか

プロダクトでの活用

競合他社との差別化要因にリソースを割きたい自社プロダクトでは、
グロースさせていきたいビジネスの概念を型(クラス)に落とし込むこと がドメインモデリングになる、と考えています。
Goにおけるドメインオブジェクト設計の考察 - 日記マン より引用

フロントエンドとの融合

このDDDとフロントエンドの関係については、次回調べていきたいと思います。
ドメイン駆動Vuexで複雑さに立ち向かう - スタディスト開発ブログ - Medium
Full-Stack JavaScript meets DDD. - Qiita

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3