Общие поддомены (Generic Subdomains) - это концепция из Domain-Driven Design (DDD), которая относится к частям домена, которые являются общими для многих систем и не являются уникальными или конкурентными преимуществами бизнеса.
Что такое Generic Subdomains?
Generic Subdomains - это:
- Части системы, которые решают стандартные, хорошо известные проблемы
- Не являются ключевыми для бизнес-логики
- Могут быть реализованы с использованием готовых решений или библиотек
- Часто требуют интеграции, а не разработки с нуля
Примеры: аутентификация, логирование, отправка email, генерация отчетов в стандартных форматах.
Отличие от Core и Supporting Subdomains
- Core Subdomain - уникальная бизнес-логика, конкурентное преимущество (например, алгоритм рекомендаций для Netflix)
- Supporting Subdomain - специфичная для бизнеса логика, но не ключевая (например, система управления контентом для медиа-компании)
- Generic Subdomain - общие функции, встречающиеся во многих системах
Примеры Generic Subdomains в Python
1. Аутентификация и авторизация
from passlib.context import CryptContext
from datetime import datetime, timedelta
import jwt
# Конфигурация хеширования паролей
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Generic Subdomain: Аутентификация
class AuthService:
@staticmethod
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
@staticmethod
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
@staticmethod
def create_access_token(data: dict, secret_key: str, expires_delta: timedelta) -> str:
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, secret_key, algorithm="HS256")
return encoded_jwt
2. Логирование (Logging)
import logging
from logging.handlers import RotatingFileHandler
import sys
# Generic Subdomain: Логирование
class LoggingService:
@staticmethod
def setup_logging(log_file: str = "app.log"):
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# Формат логов
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# Логи в файл с ротацией
file_handler = RotatingFileHandler(
log_file, maxBytes=1024*1024, backupCount=5
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# Логи в консоль
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
return logger
3. Отправка email
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# Generic Subdomain: Отправка email
class EmailService:
def __init__(self, smtp_server: str, smtp_port: int, username: str, password: str):
self.smtp_server = smtp_server
self.smtp_port = smtp_port
self.username = username
self.password = password
def send_email(self, to_email: str, subject: str, body: str, is_html: bool = False):
msg = MIMEMultipart()
msg['From'] = self.username
msg['To'] = to_email
msg['Subject'] = subject
if is_html:
msg.attach(MIMEText(body, 'html'))
else:
msg.attach(MIMEText(body, 'plain'))
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
server.starttls()
server.login(self.username, self.password)
server.send_message(msg)
4. Генерация PDF отчетов
from fpdf import FPDF
# Generic Subdomain: Генерация PDF
class PdfReportService:
@staticmethod
def generate_simple_report(data: list[dict], output_file: str):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", size=12)
# Заголовок
pdf.cell(200, 10, txt="Отчет", ln=1, align='C')
pdf.ln(10)
# Данные
for item in data:
for key, value in item.items():
pdf.cell(50, 10, txt=f"{key}:", border=1)
pdf.cell(140, 10, txt=str(value), border=1, ln=1)
pdf.ln(5)
pdf.output(output_file)
Интеграция Generic Subdomains в DDD-систему
Рассмотрим пример интеграции Generic Subdomain (EmailService) в Core Domain (OrderProcessing):
from datetime import datetime
from typing import List
# Core Subdomain: Обработка заказов
class Order:
def __init__(self, order_id: str, customer_email: str, items: List[str], total: float):
self.order_id = order_id
self.customer_email = customer_email
self.items = items
self.total = total
self.created_at = datetime.now()
self.status = "created"
class OrderProcessor:
def __init__(self, email_service: EmailService):
self.email_service = email_service
def process_order(self, order: Order):
# Бизнес-логика обработки заказа (Core Domain)
order.status = "processed"
# Использование Generic Subdomain для отправки уведомления
subject = f"Ваш заказ #{order.order_id} принят в обработку"
body = f"""
Детали заказа:
Номер: {order.order_id}
Сумма: {order.total}
Статус: {order.status}
"""
self.email_service.send_email(
to_email=order.customer_email,
subject=subject,
body=body
)
return order
# Использование
email_service = EmailService(
smtp_server="smtp.example.com",
smtp_port=587,
username="user@example.com",
password="password"
)
order_processor = OrderProcessor(email_service)
order = Order(
order_id="12345",
customer_email="customer@example.com",
items=["Товар 1", "Товар 2"],
total=100.50
)
processed_order = order_processor.process_order(order)
Стратегии реализации Generic Subdomains
- Использование готовых библиотек (как в примерах выше)
- Интеграция внешних сервисов (например, Auth0 для аутентификации)
- Собственная реализация (если требуется особая кастомизация)
Преимущества выделения Generic Subdomains
- Сосредоточение на Core Domain - команда может фокусироваться на уникальной бизнес-логике
- Снижение сложности - стандартные компоненты не нужно разрабатывать с нуля
- Упрощение поддержки - обновление Generic Subdomains независимо от основной системы
- Повторное использование - одни и те же компоненты могут использоваться в разных проектах
Заключение
Generic Subdomains в DDD позволяют эффективно организовать архитектуру системы, выделив стандартные, повторно используемые компоненты. В Python такие поддомены часто реализуются с помощью существующих библиотек (как passlib
для аутентификации или fpdf
для генерации PDF), что ускоряет разработку и повышает надежность системы.
Ключевой принцип - не изобретать велосипед для задач, которые уже имеют стандартные решения, и фокусироваться на разработке уникальной бизнес-логики, которая действительно обеспечивает конкурентное преимущество.