DDD(ドメイン駆動設計)をGoでやってみる：[初学者向け]

## はじめに

ドメイン駆動設計（DDD：Domain-Driven Design）は、ソフトウェア開発においてビジネスの核心となる「ドメイン」を中心に設計を行う手法のことです。

Goでは、シンプルなコード構造を維持しつつ、DDDの概念を適用することで保守性の高いアプリケーションを構築しやすい言語の一つです。
今回は、DDDの基本概念から、Goにおける具体的な実装方法までを初学者向けにわかりやすく解説します。
自分もこれはちゃんと覚えていきたい概念の一つです！

**対象読者**
- そもそものDDDの基本概念を学びたい方
- Goでのアプリ設計を学びたい方
- ソフトウェアの設計力を向上させたい方

**目次**
1. DDD（ドメイン駆動設計）とは？
   - DDDの基本概念
   - DDDが必要とされる理由
2. GoにおけるDDDの設計
   - パッケージ構成の例
   - エンティティ、リポジトリ、ユースケースの役割
3. 実践：GoでDDDを実装する
   - ドメインモデルの設計
   - ユースケースの実装
   - インフラストラクチャ層の設計
4. まとめ

---

## 1. DDD（ドメイン駆動設計）とは？

#### **1.1 DDDの基本概念**

DDDでは、ビジネスロジックをドメイン（Domain）に集中させ、アプリケーションの振る舞いをビジネスルールに従って設計します。

主な概念：
- **エンティティ（Entity）**：一意の識別子を持ち、状態を持つオブジェクト
- **値オブジェクト（Value Object）**：一意性を持たず、値として扱われるオブジェクト
- **リポジトリ（Repository）**：エンティティの永続化を管理
- **ユースケース（UseCase）**：アプリケーションの振る舞いを定義

#### **1.2 DDDが必要とされる理由**

DDDを採用すると、ビジネスロジックが明確になり、保守性が向上します。

| 項目 | DDDを採用しない場合 | DDDを採用した場合 |
|------|-----------------|-----------------|
| **コードの見通し** | ビジネスロジックが混在 | ドメインごとに整理 |
| **変更の影響範囲** | 影響が大きく、修正が困難 | ロジックが分離され、変更が容易 |
| **開発のスピード** | 一時的に速いが、長期的には遅くなる | 最初は時間がかかるが、拡張性が高い |

---

## 2. GoにおけるDDDの設計

#### **2.1 パッケージ構成の例**

DDDでは、アプリケーションを「レイヤー」に分けて設計します。

```
/go-ddd-example
  ├── domain          // ドメイン層（ビジネスルール）
  │   ├── user.go     // エンティティ & 値オブジェクト
  │   ├── repository.go // リポジトリインターフェース
  │   ├── service.go  // ドメインサービス
  ├── usecase         // ユースケース層
  │   ├── user_usecase.go
  ├── infrastructure  // インフラ層（データベース、外部 API）
  │   ├── user_repository.go
  ├── interface       // プレゼンテーション層（HTTP、CLI）
  │   ├── handler.go  // HTTPハンドラー
  ├── main.go         // エントリーポイント
```

#### **2.2 各レイヤーの役割**

| レイヤー | 役割 |
|----------|------|
| **ドメイン層** | ビジネスルールを定義する（エンティティ・リポジトリ） |
| **ユースケース層** | アプリケーションの具体的な操作（ユーザー作成、更新など）を定義 |
| **インフラ層** | データベースや外部APIとのやり取りを担当 |
| **プレゼンテーション層** | HTTPやCLIなどの外部インターフェースを提供 |

---

## 3. 実践：GoでDDDを実装する

#### **3.1 ドメインモデルの設計**

**domain/user.go**
```go
package domain

type User struct {
    ID   int
    Name string
}

func NewUser(id int, name string) *User {
    return &User{ID: id, Name: name}
}
```

#### **3.2 ユースケースの実装**

**usecase/user_usecase.go**
```go
package usecase

import "go-ddd-example/domain"

type UserRepository interface {
    Save(user *domain.User) error
    FindByID(id int) (*domain.User, error)
}

type UserUseCase struct {
    repo UserRepository
}

func NewUserUseCase(repo UserRepository) *UserUseCase {
    return &UserUseCase{repo: repo}
}

func (uc *UserUseCase) CreateUser(id int, name string) error {
    user := domain.NewUser(id, name)
    return uc.repo.Save(user)
}
```

#### **3.3 インフラストラクチャ層の設計**

**infrastructure/user_repository.go**
```go
package infrastructure

import (
    "errors"
    "go-ddd-example/domain"
)

type InMemoryUserRepository struct {
    users map[int]*domain.User
}

func NewInMemoryUserRepository() *InMemoryUserRepository {
    return &InMemoryUserRepository{users: make(map[int]*domain.User)}
}

func (r *InMemoryUserRepository) Save(user *domain.User) error {
    r.users[user.ID] = user
    return nil
}

func (r *InMemoryUserRepository) FindByID(id int) (*domain.User, error) {
    user, exists := r.users[id]
    if !exists {
        return nil, errors.New("user not found")
    }
    return user, nil
}
```

---

## まとめ

| 項目 | 説明 |
|------|------|
| **DDDの基本概念** | ドメインを中心に設計するアプローチ |
| **GoでのDDD構成** | ドメイン、ユースケース、インフラの3層に分割しやすい |
| **ユースケースの実装** | ビジネスルールをドメイン層に閉じ込める |

より実践的な内容にもなっているため、学習も兼ねて実際に手を動かしてみるとわかりやすいと思います！
できるエンジニアに近づけるようにこれからも頑張りたい...!