Хорошо ли когда бизнес логика не зависит от инфраструктуры
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Независимость бизнес-логики от инфраструктуры
Да, это абсолютно хорошо и является одним из ключевых принципов современной архитектуры программного обеспечения. Это фундамент чистой архитектуры, DDD (Domain-Driven Design) и SOLID принципов.
Почему это важно
Гибкость и масштабируемость — когда бизнес-логика не зависит от технических деталей, вы можете легко:
- Менять базу данных (PostgreSQL → MongoDB)
- Переходить на другой фреймворк
- Внедрять новые внешние сервисы
- Масштабировать приложение
При этом бизнес-логика остаётся неизменной и не требует переписания.
Архитектурные принципы
Clean Architecture (Чистая архитектура) предлагает следующую структуру слоёв:
┌─────────────────────────────────┐
│ Presentation (UI, API) │ ← Самый внешний слой
├─────────────────────────────────┤
│ Application (Use Cases) │
├─────────────────────────────────┤
│ Domain (Entities, Rules) │ ← Бизнес-логика
├─────────────────────────────────┤
│ Infrastructure (DB, APIs) │ ← Самый внутренний слой
└─────────────────────────────────┘
Зависимости только ВНУТРЬ (от внешних слоёв к внутренним)
Domain — это центр, который содержит чистую бизнес-логику без знания о базах данных, веб-фреймворках или HTTP.
Практический пример
# ❌ ПЛОХО: бизнес-логика зависит от инфраструктуры
from django.db import models
class User(models.Model): # Django привязка
name = models.CharField(max_length=100)
email = models.EmailField()
def process_payment(self, amount):
# Бизнес-логика смешана с ORM
if self.balance >= amount:
self.balance -= amount
self.save() # Django-зависимость!
return True
return False
# ✅ ХОРОШО: разделение concerns
# domain/user.py
class User:
"""Чистая бизнес-логика, без зависимостей от фреймворков"""
def __init__(self, name: str, email: str, balance: float):
self.name = name
self.email = email
self.balance = balance
def process_payment(self, amount: float) -> bool:
"""Бизнес-правило: можно ли списать сумму"""
if amount > 0 and self.balance >= amount:
self.balance -= amount
return True
return False
# infrastructure/repositories.py
from django.db import models
from domain.user import User
class DjangoUser(models.Model):
"""ORM модель — только для персистентности"""
name = models.CharField(max_length=100)
email = models.EmailField()
balance = models.DecimalField(max_digits=10, decimal_places=2)
class UserRepository:
"""Repository pattern — абстракция над хранилищем данных"""
def save(self, user: User) -> None:
django_user = DjangoUser.objects.get(email=user.email)
django_user.balance = user.balance
django_user.save()
def find_by_email(self, email: str) -> User:
django_user = DjangoUser.objects.get(email=email)
return User(
name=django_user.name,
email=django_user.email,
balance=float(django_user.balance)
)
# application/use_cases.py
class ProcessPaymentUseCase:
def __init__(self, repository: UserRepository):
self.repository = repository
def execute(self, email: str, amount: float) -> bool:
user = self.repository.find_by_email(email)
success = user.process_payment(amount) # Чистая бизнес-логика
if success:
self.repository.save(user)
return success
Преимущества такого подхода
- Тестируемость — легко писать unit-тесты без базы данных:
import pytest
def test_user_cannot_pay_more_than_balance():
user = User(name="John", email="john@example.com", balance=100)
assert user.process_payment(150) is False # Не зависит от DB!
assert user.balance == 100
def test_user_can_pay_within_balance():
user = User(name="John", email="john@example.com", balance=100)
assert user.process_payment(50) is True
assert user.balance == 50
- Переносимость — бизнес-логика работает везде:
- В разных фреймворках (Django, FastAPI, Flask)
- В разных БД (PostgreSQL, MongoDB, SQLite)
- В CLI, Telegram Bot, Web API
- Поддержка и эволюция — когда приходит новый разработчик, ему легче понять чистую бизнес-логику без знания всех технических деталей.
SOLID принципы в контексте
Dependency Inversion Principle — классический пример:
# Определяем абстракцию (интерфейс)
from abc import ABC, abstractmethod
class UserRepository(ABC):
@abstractmethod
def save(self, user: User) -> None:
pass
@abstractmethod
def find_by_email(self, email: str) -> User:
pass
# Конкретные реализации
class PostgresUserRepository(UserRepository):
def save(self, user: User) -> None:
# PostgreSQL-specific code
pass
class MongoUserRepository(UserRepository):
def save(self, user: User) -> None:
# MongoDB-specific code
pass
# Use case зависит от абстракции, а не от конкретной реализации
class ProcessPaymentUseCase:
def __init__(self, repository: UserRepository): # Принимаем интерфейс!
self.repository = repository
Вы можете в любой момент поменять PostgresUserRepository на MongoUserRepository без изменения ProcessPaymentUseCase.
Заключение
Чтобы ответить на вопрос: Да, это не только хорошо, но и необходимо для создания качественного, масштабируемого и поддерживаемого кода. Разделение бизнес-логики от инфраструктуры — это основа профессиональной разработки и следование современным архитектурным паттернам.