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

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

1.3 Junior🔥 111 комментариев
#Основы C# и .NET

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

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

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

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

Анемичная модель предметной области (Anemic Domain Model) — это антипаттерн в объектно-ориентированном программировании, при котором классы, представляющие бизнес-сущности (например, Order, User, Product), содержат только данные (поля/свойства) и практически не содержат бизнес-логики. Вся логика обработки этих данных выносится в отдельные сервисные классы (Service Layer), превращая сущности в простые контейнеры для данных. Это противоречит принципам богатой модели предметной области (Rich Domain Model), где сущности инкапсулируют и данные, и поведение.

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

  1. Сущности как DTO (Data Transfer Object): Классы состоят в основном из публичных свойств с get/set, напоминая структуры данных.
  2. Отсутствие инкапсуляции: Данные открыты для модификации извне, нет контроля за их состоянием.
  3. Логика в сервисах: Бизнес-правила реализованы в отдельных классах-сервисах, которые манипулируют анемичными сущностями.

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

Рассмотрим пример на C#. Анемичная сущность Order:

public class Order
{
    public int Id { get; set; }
    public string CustomerName { get; set; }
    public decimal TotalAmount { get; set; }
    public bool IsPaid { get; set; }
    public List<OrderItem> Items { get; set; } = new();
}

public class OrderItem
{
    public int ProductId { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

Сервис с бизнес-логикой:

public class OrderService
{
    public void ApplyDiscount(Order order, decimal discountPercent)
    {
        if (discountPercent < 0 || discountPercent > 50)
            throw new ArgumentException("Discount must be between 0 and 50%.");
        
        order.TotalAmount *= (1 - discountPercent / 100);
    }

    public void AddItem(Order order, OrderItem item)
    {
        if (order.IsPaid)
            throw new InvalidOperationException("Cannot modify paid order.");
        
        order.Items.Add(item);
        order.TotalAmount += item.Price * item.Quantity;
    }
}

Проблемы анемичной модели

  1. Нарушение ООП-принципов:

    • Инкапсуляция: данные открыты, их можно изменить в обход бизнес-правил.
    • Поведение отделено от данных, что усложняет понимание системы.
  2. Сложность поддержки:

    • Логика размазана по сервисам, изменения требуют правки в нескольких местах.
    • Нет гарантии целостности данных (например, можно напрямую установить order.TotalAmount = -100).
  3. Тестирование:

    • Сервисы становятся "божественными классами" с множеством зависимостей, их сложно тестировать изолированно.
  4. Отсутствие доменного языка:

    • Код не отражает реальные бизнес-процессы, становясь процедурным.

Альтернатива: Богатая модель предметной области

В богатой модели сущности инкапсулируют и данные, и поведение:

public class Order
{
    public int Id { get; private set; }
    public string CustomerName { get; private set; }
    public decimal TotalAmount { get; private set; }
    public bool IsPaid { get; private set; }
    private readonly List<OrderItem> _items = new();
    public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();

    public Order(string customerName)
    {
        CustomerName = customerName;
    }

    public void AddItem(int productId, int quantity, decimal price)
    {
        if (IsPaid)
            throw new InvalidOperationException("Cannot modify paid order.");
        
        _items.Add(new OrderItem(productId, quantity, price));
        TotalAmount += price * quantity;
    }

    public void ApplyDiscount(decimal discountPercent)
    {
        if (discountPercent < 0 || discountPercent > 50)
            throw new ArgumentException("Discount must be between 0 and 50%.");
        
        TotalAmount *= (1 - discountPercent / 100);
    }

    public void MarkAsPaid() => IsPaid = true;
}

public class OrderItem
{
    public int ProductId { get; }
    public int Quantity { get; }
    public decimal Price { get; }

    public OrderItem(int productId, int quantity, decimal price)
    {
        ProductId = productId;
        Quantity = quantity > 0 ? quantity : throw new ArgumentException("Quantity must be positive.");
        Price = price >= 0 ? price : throw new ArgumentException("Price cannot be negative.");
    }
}

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

  1. Простые CRUD-приложения без сложной бизнес-логики.
  2. Сценарии с ORM (например, Entity Framework), где сущности часто проектируются как анемичные для удобства маппинга.
  3. Архитектура CQRS, где команды и запросы разделены: для запросов используются DTO, а команды работают через агрегаты с поведением.

Заключение

Анемичная модель — это процедурный стиль в объектной одежде. Хотя она может упростить начальную разработку, в долгосрочной перспективе приводит к снижению гибкости и увеличению энтропии кода. Богатая модель, основанная на принципах DDD (Domain-Driven Design), обеспечивает лучшую инкапсуляцию, тестируемость и соответствие бизнес-требованиям. Выбор подхода зависит от сложности предметной области: для простых задач анемичная модель приемлема, для сложных — предпочтительна богатая.