Domain Model (Модель области определения)

Паттерн проектирования Domain Model

Паттерн проектирования Domain Model

Описание Domain Model

Объектная модель домена, объединяющая данные и поведение.

К сожалению, бизнес-логика приложения может быть очень сложной. Правила и логика описывают множество случаев и модификаций поведения. Для обработки всей этой сложной логики и проектируются объекты. Паттерн Domain Model (модель области определения) образует сеть взаимосвязанных объектов, в которой каждый объект представляет собой отдельную значащую сущность: может быть настолько большую, как корпорация или настолько малую, как строка из формы заказа.

Примеры реализации

// Domain Model Pattern in JavaScript
class Customer {
    constructor(id, name, email, creditLimit) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.creditLimit = creditLimit;
        this.orders = [];
    }
    
    addOrder(order) {
        if (this.canPlaceOrder(order)) {
            this.orders.push(order);
            order.setCustomer(this);
            return true;
        }
        return false;
    }
    
    canPlaceOrder(order) {
        const totalOutstanding = this.getTotalOutstanding();
        return (totalOutstanding + order.getTotal()) <= this.creditLimit;
    }
    
    getTotalOutstanding() {
        return this.orders
            .filter(order => order.status === 'pending' || order.status === 'shipped')
            .reduce((total, order) => total + order.getTotal(), 0);
    }
}

class Order {
    constructor(id, orderDate) {
        this.id = id;
        this.orderDate = orderDate;
        this.status = 'pending';
        this.customer = null;
        this.items = [];
    }
    
    addItem(product, quantity, price) {
        const item = new OrderItem(product, quantity, price);
        this.items.push(item);
        return item;
    }
    
    getTotal() {
        return this.items.reduce((total, item) => total + item.getSubtotal(), 0);
    }
    
    setCustomer(customer) {
        this.customer = customer;
    }
}

class OrderItem {
    constructor(product, quantity, price) {
        this.product = product;
        this.quantity = quantity;
        this.price = price;
    }
    
    getSubtotal() {
        return this.quantity * this.price;
    }
}

class Product {
    constructor(id, name, description, basePrice) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.basePrice = basePrice;
    }
    
    calculatePrice(quantity) {
        if (quantity >= 10) {
            return this.basePrice * 0.9; // 10% discount
        }
        return this.basePrice;
    }
}

// Usage
const customer = new Customer(1, 'John Doe', 'john@example.com', 1000);
const product = new Product(101, 'Laptop', 'Gaming Laptop', 999.99);
const order = new Order(1001, new Date());

order.addItem(product, 1, product.calculatePrice(1));

if (customer.addOrder(order)) {
    console.log('Order added successfully');
    console.log(`Order total: $${order.getTotal()}`);
} else {
    console.log('Order rejected - exceeds credit limit');
}
<?php
// Domain Model Pattern in PHP
class Customer {
    private $id, $name, $email, $creditLimit;
    private $orders = [];
    
    public function __construct($id, $name, $email, $creditLimit) {
        $this->id = $id;
        $this->name = $name;
        $this->email = $email;
        $this->creditLimit = $creditLimit;
    }
    
    public function addOrder($order) {
        if ($this->canPlaceOrder($order)) {
            $this->orders[] = $order;
            $order->setCustomer($this);
            return true;
        }
        return false;
    }
    
    public function canPlaceOrder($order) {
        $totalOutstanding = $this->getTotalOutstanding();
        return ($totalOutstanding + $order->getTotal()) <= $this->creditLimit;
    }
    
    public function getTotalOutstanding() {
        $total = 0;
        foreach ($this->orders as $order) {
            if ($order->getStatus() === 'pending' || $order->getStatus() === 'shipped') {
                $total += $order->getTotal();
            }
        }
        return $total;
        }
    }
}

class Order {
    private $id, $orderDate, $status, $customer, $items = [];
    
    public function __construct($id, $orderDate) {
        $this->id = $id;
        $this->orderDate = $orderDate;
        $this->status = 'pending';
    }
    
    public function addItem($product, $quantity, $price) {
        $item = new OrderItem($product, $quantity, $price);
        $this->items[] = $item;
        return $item;
    }
    
    public function getTotal() {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getSubtotal();
        }
        return $total;
    }
    
    public function setCustomer($customer) {
        $this->customer = $customer;
    }
    
    public function getStatus() {
        return $this->status;
    }
}

class OrderItem {
    private $product, $quantity, $price;
    
    public function __construct($product, $quantity, $price) {
        $this->product = $product;
        $this->quantity = $quantity;
        $this->price = $price;
    }
    
    public function getSubtotal() {
        return $this->quantity * $this->price;
    }
}

class Product {
    private $id, $name, $description, $basePrice;
    
    public function __construct($id, $name, $description, $basePrice) {
        $this->id = $id;
        $this->name = $name;
        $this->description = $description;
        $this->basePrice = $basePrice;
    }
    
    public function calculatePrice($quantity) {
        if ($quantity >= 10) {
            return $this->basePrice * 0.9; // 10% discount
        }
        return $this->basePrice;
    }
}

// Usage
$customer = new Customer(1, 'John Doe', 'john@example.com', 1000);
$product = new Product(101, 'Laptop', 'Gaming Laptop', 999.99);
$order = new Order(1001, new DateTime());

$order->addItem($product, 1, $product->calculatePrice(1));

if ($customer->addOrder($order)) {
    echo "Order added successfully\n";
    echo "Order total: $" . $order->getTotal() . "\n";
} else {
    echo "Order rejected - exceeds credit limit\n";
}
?>
// Domain Model Pattern in Go
package main

import (
    "fmt"
    "time"
)

type Customer struct {
    ID          int     `json:"id"`
    Name        string  `json:"name"`
    Email       string  `json:"email"`
    CreditLimit float64 `json:"creditLimit"`
    Orders      []*Order `json:"orders"`
}

func NewCustomer(id int, name, email string, creditLimit float64) *Customer {
    return &Customer{
        ID:          id,
        Name:        name,
        Email:       email,
        CreditLimit: creditLimit,
        Orders:      make([]*Order, 0),
    }
}

func (c *Customer) AddOrder(order *Order) bool {
    if c.CanPlaceOrder(order) {
        c.Orders = append(c.Orders, order)
        order.SetCustomer(c)
        return true
    }
    return false
}

func (c *Customer) CanPlaceOrder(order *Order) bool {
    totalOutstanding := c.GetTotalOutstanding()
    return (totalOutstanding + order.GetTotal()) <= c.CreditLimit
}

func (c *Customer) GetTotalOutstanding() float64 {
    total := 0.0
    for _, order := range c.Orders {
        if order.Status == "pending" || order.Status == "shipped" {
            total += order.GetTotal()
        }
    }
    return total
}

type Order struct {
    ID        int         `json:"id"`
    OrderDate time.Time   `json:"orderDate"`
    Status    string      `json:"status"`
    Customer  *Customer   `json:"customer"`
    Items     []*OrderItem `json:"items"`
}

func NewOrder(id int, orderDate time.Time) *Order {
    return &Order{
        ID:        id,
        OrderDate: orderDate,
        Status:    "pending",
        Items:     make([]*OrderItem, 0),
    }
}

func (o *Order) AddItem(product *Product, quantity int, price float64) *OrderItem {
    item := NewOrderItem(product, quantity, price)
    o.Items = append(o.Items, item)
    return item
}

func (o *Order) GetTotal() float64 {
    total := 0.0
    for _, item := range o.Items {
        total += item.GetSubtotal()
    }
    return total
}

func (o *Order) SetCustomer(customer *Customer) {
    o.Customer = customer
}

type OrderItem struct {
    Product  *Product `json:"product"`
    Quantity int      `json:"quantity"`
    Price    float64  `json:"price"`
}

func NewOrderItem(product *Product, quantity int, price float64) *OrderItem {
    return &OrderItem{
        Product:  product,
        Quantity: quantity,
        Price:    price,
    }
}

func (oi *OrderItem) GetSubtotal() float64 {
    return float64(oi.Quantity) * oi.Price
}

type Product struct {
    ID          int     `json:"id"`
    Name        string  `json:"name"`
    Description string  `json:"description"`
    BasePrice   float64 `json:"basePrice"`
}

func NewProduct(id int, name, description string, basePrice float64) *Product {
    return &Product{
        ID:          id,
        Name:        name,
        Description: description,
        BasePrice:   basePrice,
    }
}

func (p *Product) CalculatePrice(quantity int) float64 {
    if quantity >= 10 {
        return p.BasePrice * 0.9 // 10% discount
    }
    return p.BasePrice
}

// Usage
func main() {
    customer := NewCustomer(1, "John Doe", "john@example.com", 1000)
    product := NewProduct(101, "Laptop", "Gaming Laptop", 999.99)
    order := NewOrder(1001, time.Now())
    
    order.AddItem(product, 1, product.CalculatePrice(1))
    
    if customer.AddOrder(order) {
        fmt.Println("Order added successfully")
        fmt.Printf("Order total: $%.2f\n", order.GetTotal())
    } else {
        fmt.Println("Order rejected - exceeds credit limit")
    }
}

Использована иллюстрация с сайта Мартина Фаулера.

Источник