Анемичная модель и ООП для домена
Анемичный класс — класс, у которого есть только поля и нет (и не может быть) методов. ООП плохо работает с такими объектами. Это ключевой аргумент против бездумного применения ООП для бизнес-логики.
«В ~80% систем, которые массово пишутся в компаниях, объекты не имеют поведения. ООП хорош для объектов с поведением — но если поведения нет, нужно что-то другое», SR40bkadvew.
Что такое анемичная модель
Класс паспорта моделирует только информационный аспект:
class Passport {
constructor(series, number, issued) {
this.series = series;
this.number = number;
this.issued = issued;
}
}
// никаких методов — нет и не может быть. Паспорты ничего не "делают"
Класс робота — другое дело:
class Robot {
moveArm(dx, dy) { /* ... */ }
stepForward { /* ... */ }
}
// есть состояние И поведение — это уже "правильный" ООП-объект
Где ООП работает идеально
| Где | |
|---|---|
| Системное программирование | Stream, Socket, EventEmitter, Logger, Connection |
| GUI / графика | компоненты, контролы, виджеты |
| Игры | персонажи, монстры, инвентарь |
Общее: объекты с поведением и состоянием, помещающиеся в одно адресное пространство.
«Объектная модель шикарно работает в одном адресном пространстве, когда можно дёрнуть метод у кого угодно, и все данные подняты в память».
Где ООП ломается
| Почему | |
|---|---|
| Бизнес-логика / домен | данные не помещаются в RAM целиком |
| Распределённые системы | объекты живут на разных машинах |
| Склад, медицина, такси, энергетика | миллионы записей, нужно много машин |
Товар, диагноз, документ, анализы — это данные, а не поведение. Список товаров не помещается в память — нужна СУБД.
Куда положить «списать просроченное»?
Классический аргумент:
- К
Product? Не поведение товара — товар ничего не делает. - К
Worker? У него нет такого метода в модели домена. - Создать
WriteOffService? Это уже transaction script — анти-ООП паттерн.
«Списание товаров — это не поведение товара. Это поведение работника склада. А его в модели нет».
Прослойка из паттернов вокруг СУБД
Чтобы вообще как-то склеить ООП-модель и БД, придумано множество паттернов:
- Active Record — объект знает, как себя сохранить
- Repository — объект ничего не знает, репо умеет CRUD
- Data Mapper / ORM — слой между объектами и таблицами
- Query Builder — типобезопасные запросы
- Unit of Work — пакетные изменения
«Объектная модель порвана между память и БД, и нам нужно сюда привлекать всякие вещи типа Active Record, репозиториев, ORM, Query Builder. Это паттерны, которые штопают разрыв».
Иногда дополнительно — акторы, очереди сообщений для распределённости.
Альтернатива: процедуры + структуры
Для домена удобнее процедурный стиль:
// Стракты = записи БД, процедура работает с коллекцией
async function writeOffExpired(beforeDate) {
return db.query(
'DELETE FROM goods WHERE expires < $1 RETURNING *',
[beforeDate]
);
}
- Данные (
struct) и поведение (function) разделены - Часть логики в SQL, часть в JS
- Не нужно придумывать, "чей" это метод
«Я больше всего люблю процедурное программирование. Что-то на классах — потому что удобно писать на классах системный код. Для домена — процедуры и Transaction Script», vCelCNfIiBo.
Учебные примеры обманчивы
«В учебниках работа идёт с двумя-тремя слониками, машинками и прочей странной живностью — она в памяти помещается. Реальные системы — склад, такси, медицина — не помещаются.»
Двух-трёхсущностные примеры дают ложное впечатление, что ООП хорошо моделирует домен. На большом домене этот трюк перестаёт работать.
Признаки анемичной модели
- Все поля публичные, getters/setters просто читают/пишут
- Нет инкапсуляции (бизнес-правил внутри)
- Логика собрана в сервисах рядом
- Класс заменим на
Record<string, unknown>или TSinterface
В таком случае честнее использовать TypeScript-структуру или функциональный стиль, а не имитировать ООП.
Мультипарадигменный подход
«Я использую и рекомендую мультипарадигменное программирование. Что-то на классах, что-то функциональное, что-то процедурное. Парадигма — под задачу».
| Парадигма | Когда |
|---|---|
| ООП (классы) | системное, GUI, игры, объекты с поведением |
| Процедурное | бизнес-логика, ETL, скрипты |
| Функциональное | трансформации данных, чистые вычисления |
| Монадическое FP | распределённость, эффекты, бесплатное масштабирование |
Источники
- Timur · ООП плох для бизнес-процессов (2025-12-14) — основной
- Timur · ООП, процедурное, Transaction script, ORM — фрагмент семинара (2023-06-16)
- Timur · ООП построение абстракций, инкапсуляция и сокрытие (2020-03-03) — про анемичные модели
- Timur · Парадигмы программирования: ООП, ФП, процедурное (2025-06-21)
- Timur · Have Objects Failed? Or What's Wrong With OOP (2019-05-23)