スキップしてメイン コンテンツに移動

GoでDDD設計の極意

GoでDDD設計の極意

Goで実装するDDD設計の概要

近年、Go言語はマイクロサービスやクラウドネイティブアプリケーションで広く採用されており、Go応用の観点からもドメイン駆動設計(DDD)を取り入れるケースが増えています。DDD設計は、ビジネスロジックを中心に据え、ソフトウェアを「ドメイン」に分割し、開発者とビジネス担当者が共通の言語(ユビキタス言語)でコミュニケーションできるようにする手法です。GoでDDDを実装する際は、構造体とインターフェースを駆使してエンティティや値オブジェクトを表現し、パッケージ単位で境界づけられたコンテキストを構築します。

エンティティと値オブジェクトの設計

DDDにおけるエンティティは、識別子(ID)を持ち、ライフサイクルを通じて状態が変化するオブジェクトです。Goでは、構造体にIDフィールドを持たせ、メソッドでビジネスロジックを実装します。
一方、値オブジェクトは不変で、等価性は属性値で判断されます。Goでは、値オブジェクトを構造体として定義し、コピーを返すメソッドを提供することで不変性を保ちます。以下は簡単な例です。

type OrderID string

type Order struct {
    ID     OrderID
    Items  []OrderItem
    Status OrderStatus
}

type OrderItem struct {
    ProductID ProductID
    Quantity  int
    Price     Money
}

type Money struct {
    Amount   int64
    Currency string
}

このように、エンティティはIDで一意に識別され、値オブジェクトは構造体のまま不変で扱われます。

集約とリポジトリの実装

DDDでは、関連するエンティティと値オブジェクトをまとめた集約を定義し、集約ルートを通じて外部からアクセスします。Goでは、集約ルートを構造体として表現し、リポジトリインターフェースを定義して永続化を抽象化します。以下はリポジトリの例です。

type OrderRepository interface {
    Save(ctx context.Context, order *Order) error
    FindByID(ctx context.Context, id OrderID) (*Order, error)
}

実装は、例えばGORMやsqlxを使ってデータベースにアクセスし、集約ルートを永続化します。リポジトリは集約の外部インターフェースとして機能し、ドメイン層とインフラ層を分離します。

ドメインサービスとユビキタス言語

エンティティや値オブジェクトだけでは表現できないビジネスロジックは、ドメインサービスに委譲します。ドメインサービスは、複数の集約を横断する操作や、外部システムとの統合ロジックを担います。Goでは、インターフェースと構造体でサービスを実装し、依存性注入を行います。

type OrderService struct {
    repo OrderRepository
}

func (s *OrderService) PlaceOrder(ctx context.Context, items []OrderItem) (*Order, error) {
    order := NewOrder(items)
    if err := s.repo.Save(ctx, order); err != nil {
        return nil, err
    }
    return order, nil
}

また、DDDの成功はユビキタス言語の浸透に大きく依存します。開発者とビジネス担当者が同じ用語を共有し、コードベースに反映させることで、設計の齟齬を防ぎます。

境界づけられたコンテキストと実装例

DDDでは、システムを複数の境界づけられたコンテキストに分割し、各コンテキスト内で一貫したモデルを保ちます。Goでは、パッケージ単位でコンテキストを分離し、インターフェースを通じて相互作用します。以下は簡単な構成例です。

// pkg/order
package order
// Order aggregate, repository, service, etc.

// pkg/inventory
package inventory
// Inventory aggregate, repository, service, etc.

// pkg/api
package api
// HTTP handlers that use order.OrderService and inventory.InventoryService

このように、境界づけられたコンテキストを明確にすることで、変更の影響範囲を限定し、チーム間の協調をスムーズにします。Goの軽量なパッケージとインターフェースは、DDD設計を実装する上で非常に有効です。

この記事はAIによって作成されました。

コメント