← Назад к вопросам
Чем плоха зависимость бизнес-логики от низкоуровневой реализации?
2.2 Middle🔥 201 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Почему бизнес-логика не должна зависеть от низкоуровневой реализации
Это фундаментальный принцип чистой архитектуры. Зависимость бизнес-логики от реализационных деталей создаёт множество проблем. Разберу подробно.
Проблема: прямая зависимость
Пример 1: Плохо — бизнес-логика привязана к БД
# Плохо: бизнес-логика знает про SQLAlchemy и PostgreSQL
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
class OrderService:
def __init__(self):
self.engine = create_engine('postgresql://...')
def process_order(self, order_data):
# Бизнес-логика перемешана с работой с БД
session = Session(self.engine)
try:
order = Order(
user_id=order_data['user_id'],
total=order_data['total']
)
session.add(order)
session.commit()
return order.id
except SQLAlchemyError as e:
session.rollback()
raise
# Проблемы:
# 1. Нельзя протестировать без БД
# 2. Нельзя поменять БД (PostgreSQL → MongoDB)
# 3. Нельзя использовать другой ORM
# 4. Сложно добавить логирование или другие сервисы
Пример 2: Плохо — зависит от HTTP клиента
import requests
class RecommendationService:
def get_recommendations(self, user_id):
# Прямой HTTP запрос в бизнес-логике
response = requests.get(
f'https://ml-service.com/api/recommendations/{user_id}',
timeout=5
)
if response.status_code == 200:
return response.json()['items']
else:
return []
# Проблемы:
# 1. Зависит от requests библиотеки
# 2. Зависит от конкретного URL
# 3. Зависит от конкретного API формата
# 4. Сложно тестировать
# 5. Если ML сервис недоступен — падает бизнес-логика
Почему это плохо: 5 главных проблем
Проблема 1: Сложность тестирования
# Невозможно легко тестировать бизнес-логику
def test_process_order():
service = OrderService()
# Нужна реальная БД! Или придется мокировать SQLAlchemy
# Тесты становятся хрупкими и медленными
result = service.process_order({'user_id': 1, 'total': 100})
assert result is not None
# Вместо того чтобы тестировать логику, мы тестируем интеграцию
Проблема 2: Невозможность менять реализацию
# Захотели поменять PostgreSQL на MongoDB?
# Придется переписывать весь OrderService
# Захотели использовать Redis вместо SQLAlchemy?
# Переписываем ещё раз
# Каждый раз риск сломать бизнес-логику
Проблема 3: Усложнение зависимостей
# Если бизнес-логика знает про SQLAlchemy, то:
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from sqlalchemy.exc import SQLAlchemyError
# ... и это только для БД
# Плюс HTTP клиент, плюс кэширование, плюс логирование
# Результат: монолитный, нечитаемый код
Проблема 4: Сложность в командной работе
# Если бизнес-логика привязана к PostgreSQL:
# - Frontend разработчик не может запустить сервис без БД
# - Новый разработчик потребует много времени на setup
# - CI/CD пайплайн усложняется
# - Контейнеризация требует всего стека
Проблема 5: Нарушение принципов SOLID
# Зависимость обращена в неправильную сторону
# Должно быть:
# бизнес-логика -> абстракция <- реализация
# Получается:
# бизнес-логика -> конкретная реализация
# Нарушены принципы:
# - DIP (Dependency Inversion)
# - OCP (Open/Closed)
# - SRP (Single Responsibility)
Решение: Dependency Injection и абстракции
Хорошее решение 1: Используем репозитории
# Определяем абстракцию (интерфейс)
from abc import ABC, abstractmethod
class OrderRepository(ABC):
@abstractmethod
def save(self, order) -> int:
"""Сохранить заказ и вернуть ID"""
pass
@abstractmethod
def get_by_id(self, order_id: int):
"""Получить заказ по ID"""
pass
# Конкретная реализация для PostgreSQL
class PostgreSQLOrderRepository(OrderRepository):
def __init__(self, session: Session):
self.session = session
def save(self, order) -> int:
self.session.add(order)
self.session.commit()
return order.id
def get_by_id(self, order_id: int):
return self.session.query(Order).filter_by(id=order_id).first()
# Другая реализация для MongoDB
class MongoDBOrderRepository(OrderRepository):
def __init__(self, db):
self.db = db
def save(self, order) -> int:
result = self.db.orders.insert_one(order.to_dict())
return result.inserted_id
def get_by_id(self, order_id: int):
return self.db.orders.find_one({'_id': order_id})
# Бизнес-логика: зависит только от абстракции
class OrderService:
def __init__(self, repository: OrderRepository):
# Dependency Injection - получаем абстракцию
self.repository = repository
def process_order(self, order_data) -> int:
# Чистая бизнес-логика, не знает про БД
order = Order(
user_id=order_data['user_id'],
total=order_data['total'],
status='pending'
)
return self.repository.save(order)
# Преимущества:
# - Бизнес-логика не знает про конкретную БД
# - Легко менять PostgreSQL на MongoDB
# - Легко тестировать с mock репозиторием
# - Код чистый и понятный
Тестирование стало простым
# Mock репозиторий для тестирования
class MockOrderRepository(OrderRepository):
def __init__(self):
self.orders = {}
self.next_id = 1
def save(self, order) -> int:
order_id = self.next_id
self.orders[order_id] = order
self.next_id += 1
return order_id
def get_by_id(self, order_id: int):
return self.orders.get(order_id)
# Тест становится простым и быстрым
def test_process_order():
repo = MockOrderRepository()
service = OrderService(repo)
order_id = service.process_order({'user_id': 1, 'total': 100})
assert order_id == 1
assert repo.orders[1].total == 100
# Нет зависимости от БД, нет медленных тестов
Хорошее решение 2: Используем абстракции для внешних сервисов
# Абстракция для рекомендаций
from abc import ABC, abstractmethod
class RecommendationProvider(ABC):
@abstractmethod
def get_recommendations(self, user_id: int) -> list:
pass
# Реализация через HTTP
class MLServiceRecommendationProvider(RecommendationProvider):
def __init__(self, base_url: str, timeout: int = 5):
self.base_url = base_url
self.timeout = timeout
def get_recommendations(self, user_id: int) -> list:
response = requests.get(
f'{self.base_url}/recommendations/{user_id}',
timeout=self.timeout
)
if response.status_code == 200:
return response.json()['items']
return []
# Альтернативная реализация через кэш
class CachedRecommendationProvider(RecommendationProvider):
def __init__(self, provider: RecommendationProvider, cache):
self.provider = provider
self.cache = cache
def get_recommendations(self, user_id: int) -> list:
cache_key = f'recommendations:{user_id}'
cached = self.cache.get(cache_key)
if cached:
return cached
recommendations = self.provider.get_recommendations(user_id)
self.cache.set(cache_key, recommendations, ttl=3600)
return recommendations
# Бизнес-логика
class UserService:
def __init__(self, provider: RecommendationProvider):
self.provider = provider
def get_user_feed(self, user_id: int) -> dict:
recommendations = self.provider.get_recommendations(user_id)
# Чистая бизнес-логика
return {
'user_id': user_id,
'recommendations': recommendations,
'count': len(recommendations)
}
Правильная архитектура: слои
# Слои должны быть так расположены:
#
# Presentation (HTTP, CLI, WebSocket)
# ↓
# Application (Бизнес-сценарии)
# ↓
# Domain (Чистая бизнес-логика)
# ↑
# Infrastructure (БД, HTTP, кэши)
#
# Зависимости идут ВНИЗ к абстракциям,
# а не в сторону конкретных реализаций
# Domain слой НЕ знает про:
# - SQLAlchemy
# - Requests
# - Redis
# - FastAPI
# Только про абстракции!
Практический пример: полная структура
# domain/models/order.py
class Order:
def __init__(self, user_id: int, total: float):
self.user_id = user_id
self.total = total
self.status = 'pending'
def approve(self):
self.status = 'approved'
def cancel(self):
self.status = 'cancelled'
# domain/repositories.py
from abc import ABC, abstractmethod
class OrderRepository(ABC):
@abstractmethod
def save(self, order: Order) -> int:
pass
# application/services/order_service.py
class OrderService:
def __init__(self, repository: OrderRepository):
self.repository = repository
def process_order(self, user_id: int, total: float) -> int:
order = Order(user_id, total)
order.approve()
return self.repository.save(order)
# infrastructure/repositories/postgres_order_repository.py
from sqlalchemy.orm import Session
class PostgreSQLOrderRepository(OrderRepository):
def __init__(self, session: Session):
self.session = session
def save(self, order: Order) -> int:
db_order = OrderModel(**order.__dict__)
self.session.add(db_order)
self.session.commit()
return db_order.id
# presentation/api/endpoints.py
from fastapi import APIRouter, Depends
router = APIRouter()
@router.post('/orders')
def create_order(order_data: dict, service: OrderService = Depends()):
order_id = service.process_order(
user_id=order_data['user_id'],
total=order_data['total']
)
return {'order_id': order_id}
Заключение
Главное правило: Зависимости должны быть ОБРАЩЕНЫ к абстракциям, а не к конкретным реализациям.
Когда бизнес-логика не зависит от деталей реализации:
- Легко тестировать
- Легко менять технологии
- Код остается чистым
- Команда работает быстрее
- Система легче расширяется
Это основа чистой архитектуры и SOLID принципов.