👀
ドメインモデルとデータモデルの違いをGo言語で解説
はじめに
ソフトウェア開発において、ドメインモデルとデータモデルの理解は非常に重要です。これらはしばしば混同されがちですが、役割と目的が異なります。
本記事では、オンライン注文システムを例にこれらの違いを明確に説明します。
ドメインモデルとは?
ドメインモデルは、ビジネスのロジックや規則を表すオブジェクトモデルです。これは、アプリケーションが解決しようとしているビジネス問題の抽象表現です。
オンライン注文システムのコンテキストでは、以下のOrder、OrderItem、Productなどがドメインモデルの典型的な例です。
Order
注文に関連するビジネスロジックを表すオブジェクト。
type Order struct {
ID string
CustomerID string
Items []OrderItem
Status OrderStatus
TotalPrice float64
}
func (o *Order) AddItem(item OrderItem) {
o.Items = append(o.Items, item)
}
func (o *Order) CalculateTotal() float64 {
total := 0.0
for _, item := range o.Items {
total += item.Price * float64(item.Quantity)
}
return total
}
type OrderStatus string // 例: "新規", "発送済み"
OrderItem
注文内の各アイテムを表します。
type OrderItem struct {
Product Product
Quantity int
Price float64
}
Product
製品情報を表すオブジェクト。
type Product struct {
ID string
Name string
Price float64
}
ProductOrder
顧客からの注文情報を受け取るためのオブジェクト。
type ProductOrder struct {
ProductID string
Quantity int
}
データモデルとは?
データモデルは、データベースの構造やデータの形式を定義するモデルです。データ永続化のために使用され、アプリケーションが操作するデータの物理的表現を提供します。
OrderModel
データベースの注文テーブルを反映する構造体。
type OrderModel struct {
ID string `db:"id"`
CustomerID string `db:"customer_id"`
Status string `db:"status"`
TotalPrice float64 `db:"total_price"`
}
OrderItemModel
データベースの注文アイテムテーブルを反映する構造体。
type OrderItemModel struct {
ID string `db:"id"`
OrderID string `db:"order_id"`
ProductID string `db:"product_id"`
Quantity int `db:"quantity"`
Price float64 `db:"price"`
}
ProductModel
データベースの製品テーブルを反映する構造体。
type ProductModel struct {
ID string `db:"id"`
Name string `db:"name"`
Price float64 `db:"price"`
}
モデルの使い分け
オンライン注文システムにおいて、ドメインモデルはビジネスロジックの処理に、データモデルはデータの保存や取得に使用されます。
製品情報取得関数
データベースから製品情報を取得する処理。
// GetProductByID は指定された製品IDに基づいて製品情報をデータベースから取得します。
func GetProductByID(db *sql.DB, productID string) (Product, error) {
// データベースから製品情報を取得するSQLクエリ
query := `SELECT id, name, price FROM products WHERE id = $1`
// クエリの実行
row := db.QueryRow(query, productID)
// ProductModel に対応する構造体
var pm ProductModel
// クエリ結果のスキャン
err := row.Scan(&pm.ID, &pm.Name, &pm.Price)
if err != nil {
if err == sql.ErrNoRows {
// 製品が見つからない場合のエラーハンドリング
return Product{}, err
}
// その他のエラーハンドリング
return Product{}, err
}
// ProductModel から Product ドメインモデルへの変換
product := Product{
ID: pm.ID,
Name: pm.Name,
Price: pm.Price,
}
return product, nil
}
注文作成関数
顧客からの注文を受けて、注文を生成し、データベースに保存する処理。
func CreateOrder(db *sql.DB, customerID string, productOrders []ProductOrder) (*Order, error) {
order := Order{
ID: GenerateOrderID(),
CustomerID: customerID,
Status: "新規",
}
// データベーストランザクションの開始
tx, err := db.Begin()
if err != nil {
return nil, err
}
// 各注文アイテムに対する処理
for _, po := range productOrders {
product, err := GetProductByID(db, po.ProductID) // GetProductByID は上記で定義
if err != nil {
tx.Rollback()
return nil, err
}
item := OrderItem{
Product: product,
Quantity: po.Quantity,
Price: product.Price,
}
order.AddItem(item)
// OrderItemModel を使用して注文アイテムをデータベースに保存
itemModel := OrderItemModel{
OrderID: order.ID,
ProductID: item.Product.ID,
Quantity: item.Quantity,
Price: item.Price,
}
itemInsertQuery := `INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)`
_, err = tx.Exec(itemInsertQuery, itemModel.OrderID, itemModel.ProductID, itemModel.Quantity, itemModel.Price)
if err != nil {
tx.Rollback()
return nil, err
}
}
// 注文のデータモデルの準備
orderModel := OrderModel{
ID: order.ID,
CustomerID: order.CustomerID,
Status: string(order.Status),
TotalPrice: order.CalculateTotal(), // 総額を計算
}
// 注文をデータベースに保存
orderInsertQuery := `INSERT INTO orders (id, customer_id, status, total_price) VALUES ($1, $2, $3, $4)`
_, err = tx.Exec(orderInsertQuery, orderModel.ID, orderModel.CustomerID, orderModel.Status, orderModel.TotalPrice)
if err != nil {
tx.Rollback()
return nil, err
}
// トランザクションのコミット
if err := tx.Commit(); err != nil {
return nil, err
}
return &order, nil
}
まとめ
ドメインモデルとデータモデルは、ソフトウェア開発において異なる役割を果たします。
ドメインモデルはビジネスロジックと操作を、データモデルはデータの永続化と構造を担当します。これらの違いを理解し、適切に使い分けることで、より効率的で保守しやすいソフトウェアを開発することが可能になると考えてます。
Discussion