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

В чем разница между доменной сложностью и привнесённой?

2.0 Middle🔥 121 комментариев
#Архитектура и паттерны

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

Разница между доменной сложностью и привнесённой сложностью

Это важный концепт для архитектуры ПО и принятия технических решений. На русском часто называют "врождённой" и "случайной" сложностью (из книги "No Silver Bullet" Фреда Брукса).

Быстрый ответ

Доменная сложность (Essential Complexity):
- Сложность САМОЙ ПРОБЛЕМЫ
- Это неизбежно, нельзя избежать
- Нужно решить её, чтобы получить результат
- Пример: спроектировать микрочип с 10 млрд транзисторов

Привнесённая сложность (Accidental Complexity):
- Сложность РЕШЕНИЯ (как мы решаем)
- Это можно и нужно избегать
- Результат плохого дизайна, выбора технологий, процессов
- Пример: использование 50 разных framework'ов для одного проекта

1. Определение доменной сложности (Essential)

Это сложность, которая НЕОТЪЕМЛЕМА от проблемы, которую мы решаем.

# Пример 1: e-commerce платформа
# Доменная сложность включает:
# - Инвентарь товаров (нужно отслеживать кол-во)
# - Заказы (нужно управлять жизненный цикл)
# - Платежи (нужна интеграция с платёжными системами)
# - Доставка (координация с логистикой)
# - Возвраты (обработка возвратов)
# - Налоги (разные налоги в разных странах)
# Это сложность самого бизнеса, не способа реализации!

class Product:
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

class Order:
    def __init__(self, user_id, items):
        self.user_id = user_id
        self.items = items  # list[Product]
        self.status = "pending"
    
    def calculate_total(self):
        return sum(item.price * qty for item, qty in self.items)
    
    def apply_taxes(self, tax_rate):
        return self.calculate_total() * (1 + tax_rate)
    
    def process_payment(self, payment_info):
        # Интеграция с платёжной системой
        pass
    
    def arrange_shipping(self, address):
        # Интеграция с логистикой
        pass
    
    # Эта сложность НЕИЗБЕЖНА - нужно её решить

# Пример 2: система рекомендаций
# Доменная сложность:
# - Алгоритм вычисления схожести (рекомендация похожих товаров)
# - Учёт истории пользователя
# - A/B тестирование рекомендаций
# - Обработка cold-start problem (новые пользователи)
# - Масштабирование на миллионы товаров
# Эта сложность присуща самой проблеме рекомендаций

2. Определение привнесённой сложности (Accidental)

Это сложность, которая мы САМИ добавили, выбирая неправильные инструменты или подход.

# Пример 1: использование неправильного инструмента
# Привнесённая сложность:

# ❌ ПЛОХО: использовать Excel для БД с миллионом строк
# - Открывать файл 30 секунд
# - Медленные вычисления
# - Нет поддержки конкурентного доступа
# - Нет backup'ов

# ✅ ПРАВИЛЬНО: использовать PostgreSQL
# Убирает привнесённую сложность! (но доменная остаётся)

# Пример 2: плохая архитектура
# ❌ ПЛОХО: написать всё в одном файле (monolith)
app.py (5000 строк):
    - HTTP handlers
    - Business logic
    - Database queries
    - Email sending
    - Payment processing
    - File uploads
    - Authentication
    # Невозможно понять какой класс за что отвечает
    # Привнесённая сложность!

# ✅ ПРАВИЛЬНО: разделить по слоям (clean architecture)
app/
  ├── presentation/    # HTTP interface
  ├── application/     # Business logic
  ├── domain/          # Models
  └── infrastructure/  # Database, emails, payments
# Проще ориентироваться, тестировать, масштабировать
# Убирает привнесённую сложность!

# Пример 3: неправильные зависимости
# ❌ ПЛОХО: зависимость от внешней библиотеки для простой функции
from heavy_library import calculate_age

def get_user_age(birth_date):
    return calculate_age(birth_date)  # Простая функция требует huge библиотеку

# ✅ ПРАВИЛЬНО: написать самому
def get_user_age(birth_date):
    from datetime import date
    return (date.today() - birth_date).days // 365

# Убрали привнесённую сложность! (и зависимость)

3. Визуальное сравнение

┌─────────────────────────────────────────────────────┐
│ Доменная сложность (Essential)                      │
│                                                     │
│ - Требования бизнеса                                │
│ - Алгоритмы для решения проблемы                    │
│ - Трудные математические задачи                     │
│ - Правила бизнес-логики                             │
│ - Невозможно избежать                               │
│ - ДОЛЖНЫ решить её, чтобы доставить ценность        │
└─────────────────────────────────────────────────────┘

ВНА ВЫПОЛНЕННОГО ПРОЕКТА (100%)
  = Доменная сложность (50-70%)
  + Привнесённая сложность (30-50%)
  
  Хороший архитект минимизирует привнесённую!

┌─────────────────────────────────────────────────────┐
│ Привнесённая сложность (Accidental)                 │
│                                                     │
│ - Плохой выбор технологий                           │
│ - Плохая архитектура                                │
│ - Дублирование кода                                 │
│ - Неправильные зависимости                          │
│ - Технический долг                                  │
│ - МОЖНО избежать                                    │
│ - Результат плохого дизайна                         │
└─────────────────────────────────────────────────────┘

4. Примеры в реальном коде

Пример 1: Система учёта сотрудников

# ДОМЕННАЯ сложность (обязательна):
# - Расчёт зарплаты с налогами
# - Управление отпусками
# - Отслеживание часов работы
# - Управление льготами
# Это сложность самого бизнеса!

class PayrollCalculator:
    def calculate_salary(self, employee, hours_worked, overtime_hours):
        base_pay = employee.hourly_rate * hours_worked
        overtime_pay = employee.hourly_rate * 1.5 * overtime_hours
        gross_salary = base_pay + overtime_pay
        
        # Налоги зависят от страны/штата
        taxes = self._calculate_taxes(gross_salary, employee.tax_bracket)
        benefits_deduction = self._calculate_benefits(employee.benefits)
        
        net_salary = gross_salary - taxes - benefits_deduction
        return {
            'gross': gross_salary,
            'taxes': taxes,
            'benefits': benefits_deduction,
            'net': net_salary
        }
    
    def _calculate_taxes(self, salary, tax_bracket):
        # Сложная логика с льготами и прогрессивными налогами
        pass
    
    def _calculate_benefits(self, benefits):
        # Вычисление вычитаемых льгот
        pass

# ПРИВНЕСЁННАЯ сложность (можно избежать):
# ❌ ПЛОХО: использовать Excel
# ❌ ПЛОХО: писать SQL запросы в HTML файлах
# ❌ ПЛОХО: хранить данные в CSV
# ✅ ПРАВИЛЬНО: использовать БД + API + структурированный код

Пример 2: Микросервисная архитектура

# ДОМЕННАЯ сложность:
# - Обработка заказов (сложный бизнес-процесс)
# - Управление инвентарём
# - Интеграция платежей

class OrderService:
    def create_order(self, items, user_id):
        # Доменная сложность: проверка наличия товара
        inventory = self.check_inventory(items)
        if not inventory:
            raise OutOfStockError()
        
        # Доменная сложность: расчёт доставки
        shipping_cost = self.calculate_shipping(items, user.address)
        
        # Доменная сложность: обработка платежа
        payment = self.process_payment(items, shipping_cost)
        
        order = Order.create(items, payment)
        return order

# ПРИВНЕСЁННАЯ сложность (можно минимизировать):
# ❌ ПЛОХО: микросервисная архитектура для маленького проекта
# ❌ ПЛОХО: использовать 5 разных языков программирования
# ❌ ПЛОХО: распределённые транзакции через RabbitMQ
# ✅ ПРАВИЛЬНО: monolith с хорошей архитектурой

# Привнесённая сложность растёт с каждым микросервисом:
# - Сетевые задержки
# - Распределённые транзакции
# - Версионирование API
# - Мониторинг
# Это не надо добавлять, если доменная сложность это не требует!

5. Как минимизировать привнесённую сложность

# 1. YAGNI (You Aren't Gonna Need It)
# ❌ ПЛОХО: добавлять функции "на будущее"

class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email
        self.bio = None
        self.phone = None
        self.address = None
        self.preferences = {}
        self.settings = {}
        self.metadata = {}
        # 50 полей, которые "может быть понадобятся"
        # Привнесённая сложность!

# ✅ ПРАВИЛЬНО: добавлять только нужное
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email
    
    # Добавим потом, когда понадобится

# 2. DRY (Don't Repeat Yourself)
# ❌ ПЛОХО: одна логика в 10 местах

def validate_email_1():
    return '@' in email and '.' in email.split('@')[1]

def validate_email_2():
    return '@' in email and '.' in email.split('@')[1]

def validate_email_3():
    return '@' in email and '.' in email.split('@')[1]

# ✅ ПРАВИЛЬНО: одна функция
def validate_email(email: str) -> bool:
    return '@' in email and '.' in email.split('@')[1]

# 3. Правильный выбор инструментов
# ❌ ПЛОХО: использовать GraphQL когда нужен REST
# ❌ ПЛОХО: использовать MongoDB для реляционных данных
# ✅ ПРАВИЛЬНО: выбрать инструмент по задаче

# 4. Clean Code
# ❌ ПЛОХО: имена переменных x, y, z
for x in users:
    if x[5] > 100:
        y.append(x[0])

# ✅ ПРАВИЛЬНО: понятные имена
for user in users:
    if user['balance'] > 100:
        premium_users.append(user['id'])

# 5. Тестируемость
# ❌ ПЛОХО: всё намешано в одном классе
class UserService:
    def register_user(self, email):
        # HTTP запрос
        # Парсинг JSON
        # Валидация
        # Сохранение в БД
        # Отправка email
        # Логирование
        # Все вместе - невозможно тестировать!

# ✅ ПРАВИЛЬНО: разделить на слои
class RegisterUserUseCase:
    def execute(self, email: str) -> User:  # Только бизнес-логика
        pass

@app.post('/register')
def register_endpoint(request):  # Только HTTP
    pass

6. Когда привнесённая сложность оправдана

# Иногда стоит добавить привнесённую сложность, чтобы:

# 1. Миграция на лучший инструмент
# ❌ Старо: Python 2
# ✅ Ново: Python 3
# Это добавляет временную привнесённую сложность
# Но окупается долгосрочно

# 2. Масштабирование требует архитектуры
# ❌ Монолит достаточен для 1000 пользователей
# ✅ Микросервисы нужны для 1 млн пользователей
# Добавляем привнесённую сложность потому что доменная требует масштаба

# 3. Требования безопасности
# ❌ Простая аутентификация для локального приложения
# ✅ OAuth, 2FA для production
# Добавляем сложность потому что бизнес требует

# Правило: добавляй привнесённую сложность ТОЛЬКО если она решает реальную доменную проблему

7. Как определить, какая сложность где

# Вопрос 1: "Нужно ли это для решения проблемы?"
# ДА → Доменная сложность (нужна)
# НЕТ → Привнесённая сложность (убери)

# Вопрос 2: "Это требование бизнеса?"
# ДА → Доменная
# НЕТ → Привнесённая (возможно убрать)

# Вопрос 3: "Может ли быть проще?"
# ДА → Привнесённая (упростить!)
# НЕТ → Доменная (необходимо)

# Пример: e-commerce
# "Нужно подерживать 10 валют" → Доменная (бизнес требует)
# "Написать на 5 языках программирования" → Привнесённая (выбери 1)
# "Сложные алгоритмы рекомендаций" → Доменная (бизнес требует)
# "Использовать самый новый framework" → Привнесённая (выбери проверенный)

8. Практическое применение

# Архитектурное решение:

# На день 1: Минимальное решение
# - Монолит на Flask
# - PostgreSQL
# - Простые endpoints
# Минимум привнесённой сложности!

app = Flask(__name__)

@app.route('/users', methods=['POST'])
def create_user():
    email = request.json['email']
    if not validate_email(email):
        return {'error': 'Invalid email'}, 400
    user = User(email=email)
    db.session.add(user)
    db.session.commit()
    return {'id': user.id}, 201

# На день 100: Когда доменная сложность требует масштаба
# - Микросервисы
# - Message queues
# - Caching
# - Мониторинг
# Добавляем привнесённую сложность потому что масштаб требует

# НО! Только когда действительно нужно!

9. Вывод

Доменная сложность (Essential):

  • Неизбежна
  • Это сложность самой проблемы
  • Должна быть решена
  • Пример: расчёт налогов в e-commerce

Привнесённая сложность (Accidental):

  • Результат наших выборов
  • Должна быть МИНИМИЗИРОВАНА
  • Пример: использование 5 framework'ов для одного проекта

Правило архитектора:

Минимизируй привнесённую сложность
Концентрируйся на доменной сложности
Добавляй сложность только когда она решает реальную проблему

Цитата Фреда Брукса:

"Нет серебряной пули. Нельзя побороть доменную сложность, но можно минимизировать привнесённую."

Хороший разработчик знает разницу и выбирает инструменты, которые минимизируют привнесённую сложность, позволяя сконцентрироваться на доменной!