← Назад к вопросам

Что такое анемичная модель?

1.0 Junior🔥 21 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое анемичная модель (Anemic Domain Model)?

Анемичная модель — это объектная модель, в которой классы данных содержат только состояние (поля, свойства) и практически не содержат поведения (методов бизнес-логики). Эти классы являются простыми структурами для передачи данных между слоями приложения, а вся логика работы с ними сосредоточена в отдельных сервисах, менеджерах или контроллерах.

Этот термин был популяризирован Мартином Фаулером в его статье «AnemicDomainModel», где он критиковал такой подход как анти-паттерн в контексте объектно-ориентированного дизайна.

Ключевые характеристики анемичной модели

  • Отсутствие поведения: Классы представляют собой набор публичных свойств (get/set) без методов, реализующих бизнес-правила или инварианты.
  • Разделение данных и логики: Бизнес-операции выполняются внешними классами-сервисами, которые манипулируют этими "пустыми" объектами.
  • Схожесть с DTO: Часто такие модели используются исключительно для передачи данных (как Data Transfer Object), но применяются повсеместно в доменном слое.

Пример анемичной модели в Swift

Рассмотрим типичную модель заказа в приложении.

// Анемичная модель: только данные, нет логики
struct Order {
    var id: UUID
    var items: [OrderItem]
    var totalAmount: Decimal
    var status: OrderStatus // e.g., .pending, .paid, .shipped
}

Все операции с этим заказом будут выполняться в другом месте:

// Сервис, содержащий всю бизнес-логику
class OrderService {
    func calculateTotal(for order: Order) -> Decimal {
        // Вычисление суммы, возможно с применением скидок, налогов
        let itemsTotal = order.items.reduce(0) { $0 + $1.price }
        // ... сложная логика
        order.totalAmount = itemsTotal
        return itemsTotal
    }

    func markAsPaid(_ order: Order) {
        // Проверка возможности оплаты, изменение статуса
        guard order.status == .pending else { return }
        order.status = .paid
        // ... дополнительные действия: обновление склада, отправка уведомления
    }

    func validate(_ order: Order) -> Bool {
        // Проверка корректности заказа
        return !order.items.isEmpty && order.totalAmount > 0
    }
}

Сравнение с "насыщенной" (Rich) моделью

В противоположность анемичной, "насыщенная" доменная модель инкапсулирует данные и поведение внутри одного класса.

// Насыщенная модель: данные и логика вместе
struct Order {
    private(set) var id: UUID
    private(set) var items: [OrderItem]
    private(set) var totalAmount: Decimal
    private(set) var status: OrderStatus

    init(items: [OrderItem]) {
        self.id = UUID()
        self.items = items
        self.status = .pending
        self.calculateTotal() // Логика внутри модели
    }

    // Бизнес-логика инкапсулирована в методы модели
    private mutating func calculateTotal() {
        let baseTotal = items.reduce(0) { $0 + $1.price }
        // Применение правил расчета внутри модели
        self.totalAmount = applyDiscounts(to: baseTotal)
    }

    mutating func markAsPaid() throws {
        guard status == .pending else {
            throw OrderError.invalidStateTransition
        }
        // Все проверки и побочные эффекты управляются здесь
        status = .paid
        // Модель может управлять своими внутренними изменениями
        updateInventory()
    }

    func validate() -> Bool {
        return !items.isEmpty && totalAmount > 0
    }

    // Приватные методы для внутренней логики
    private func applyDiscounts(to amount: Decimal) -> Decimal { ... }
    private func updateInventory() { ... }
}

Почему анемичная модель считается проблемой?

  1. Нарушение принципов ООП: Объектно-ориентированный дизайн предполагает объединение данных и поведения. Анемичная модель превращает объекты в простые структуры, сводя ООП к процедурному стилю.
  2. Потеря инкапсуляции: Бизнес-правила и валидация оказываются снаружи, что позволяет изменять состояние объекта в неконтролируемых комбинациях из разных сервисов. Это ведет к:
    *   **Распылению логики:** Одни и те же правила могут быть дублированы или противоречить друг другу в разных сервисах.
    *   **Сложности поддержки инвариантов:** Гарантировать корректное состояние объекта (например, `totalAmount` всегда соответствует сумме `items`) становится трудно.
  1. Снижение выразительности модели: Модель не "говорит" о своих возможностях. Вместо естественного order.markAsPaid() мы видим orderService.markAsPaid(order).

Когда анемичная модель может быть приемлемой?

  • Простой CRUD: Для приложений без сложной бизнес-логики, где сущности действительно являются лишь данными для записи/чтения (например, админ-панель).
  • Слои персистентности или представления: Использование простых структур как DTO для передачи между API и UI или между базой данных и сервисом — это нормальная практика. Проблема возникает, когда эти DTO становятся единственным представлением домена в системе.
  • Производительность или ограничения: В некоторых архитектурах (например, строгий Clean Architecture) разделение данных и логики может быть сознательным выбором для обеспечения тестируемости или изоляции слоев.

Заключение

Анемичная модель — это паттерн, который следует применять с осторожностью. Для сложных систем с богатой бизнес-логикой он приводит к хрупкости, дублированию и нарушению инкапсуляции. Разработчик должен стремиться к созданию "насыщенных" моделей, где объекты являются полноценными участниками системы, отвечающими за свое состояние и поведение. Однако в простых случаях или на границах слоев приложения использование простых структур данных может быть оправданным и практичным. Ключ — в понимании контекста и сознательном выборе подхода.