Какие знаешь архитектуры построения приложений?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Архитектуры построения приложений
За 10+ лет я работал с разными архитектурными паттернами. Каждый имеет свои плюсы и минусы.
1. Monolithic Architecture (Монолит)
Описание: все компоненты в одном приложении
┌─────────────────────────────────┐
│ Single Application │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ Auth │ │Users │ │Orders│ │
│ └──────┘ └──────┘ └──────┘ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ Carts│ │Prods │ │Logic │ │
│ └──────┘ └──────┘ └──────┘ │
│ │
│ One Database (PostgreSQL) │
└─────────────────────────────────┘
Когда начинать:
- MVP и стартапы
- < 10 разработчиков
- Один основной домен
Плюсы:
- Просто развивать и деплоить
- Легко тестировать
- Хорошая производительность (нет network latency)
- Транзакции работают просто
Минусы:
- При 100k строк очень сложно разобраться
- Сложно масштабировать разные части
- Один падает — всё падает
- Разработчики мешают друг другу
Когда переходить: когда видишь natural boundaries между модулями.
2. Microservices Architecture
Описание: множество независимых сервисов, каждый со своей БД
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Auth Service │ │ Users Service│ │ Orders Service
│ │ │ │ │ │
│ PostgreSQL │ │ PostgreSQL │ │ PostgreSQL │
└──────────────┘ └──────────────┘ └──────────────┘
↑ ↑ ↑
└────────────────┼────────────────────┘
API Gateway / Event Bus (RabbitMQ)
Правило: не делай микросервисы, если нет 10+ разработчиков.
Плюсы:
- Независимые deploy и scaling
- Разные tech stack для разных сервисов
- Легче тестировать отдельно
- Разработчики не мешают друг другу
Минусы:
- Distributed system complexity (все проблемы распределённых систем)
- Network latency
- Consistency problems (CAP theorem)
- Сложно отлаживать
- DevOps nightmare (10+ контейнеров)
Когда использовать:
- Когда монолит становится bottleneck
- Разные части масштабируются по-разному
- Разные команды работают на разные части
3. Layered Architecture (слоистая) — классическая Django
Описание: разделение на слои: Presentation → Application → Domain → Infrastructure
# Presentation Layer (views, API endpoints)
from django.http import JsonResponse
def create_order(request):
# Парсить input, валидировать
user_id = request.data['user_id']
items = request.data['items']
# Вызвать application layer
order = order_service.create_order(user_id, items)
# Вернуть response
return JsonResponse({'order_id': order.id})
# Application Layer (business logic, use cases)
class CreateOrderService:
def create_order(self, user_id, items):
user = self.user_repository.get(user_id)
if not user:
raise UserNotFound()
# Бизнес логика
total = sum(item.price for item in items)
order = Order(user=user, total=total)
# Сохранить
self.order_repository.save(order)
return order
# Domain Layer (entities, value objects)
class Order(models.Model):
user = models.ForeignKey(User)
items = models.ManyToManyField(Item)
total = models.DecimalField()
created = models.DateTimeField(auto_now_add=True)
# Infrastructure Layer (database, external APIs)
class OrderRepository:
def save(self, order):
order.save()
def get(self, order_id):
return Order.objects.get(id=order_id)
Плюсы:
- Логичное разделение ответственности
- Легко тестировать каждый слой отдельно
- Классический подход, все его знают
Минусы:
- Может стать слишком fat (толстый application layer)
- Зависимость вниз, но сложна зависимость вверх
4. Clean Architecture (по Bob Martin)
Описание: зависимости направлены к центру (к бизнес логике)
Entities (Pure Business Logic)
↑
Use Cases (Application)
↑
Interface Adapters (Controllers, Presenters)
↑
Frameworks & Drivers
Правило: внешние слои зависят от внутренних, не наоборот.
# Entities Layer (Domain models)
class User:
def __init__(self, id, name, email):
self.id = id
self.name = name
self.email = email
def is_valid_email(self):
return '@' in self.email
# Use Cases Layer
class CreateUserUseCase:
def __init__(self, user_repository: IUserRepository):
self.repository = user_repository
def execute(self, name: str, email: str):
user = User(id=None, name=name, email=email)
if not user.is_valid_email():
raise InvalidEmail()
saved_user = self.repository.save(user)
return saved_user
# Interface Adapters (Controllers)
class UserController:
def __init__(self, use_case: CreateUserUseCase):
self.use_case = use_case
def create(self, request):
result = self.use_case.execute(
name=request.data['name'],
email=request.data['email']
)
return JsonResponse({'user_id': result.id})
# Infrastructure (Django, PostgreSQL)
class DjangoUserRepository(IUserRepository):
def save(self, user: User):
django_user = UserModel(
name=user.name,
email=user.email
)
django_user.save()
return user
Плюсы:
- Бизнес логика не зависит от фреймворка
- Очень тестируемо
- Легко менять реализацию (PostgreSQL → MongoDB)
Минусы:
- Много boilerplate кода (interfaces, adapters)
- Оverkill для простых приложений
- Сложно новичкам
5. Domain-Driven Design (DDD)
Описание: сосредоточение на домене (бизнес), не на технологии
# Aggregates (корневые сущности)
class Order:
def __init__(self, order_id, customer_id):
self.order_id = order_id
self.customer_id = customer_id
self.items = []
self.status = 'pending'
def add_item(self, product_id, quantity):
if self.status != 'pending':
raise InvalidOrderState()
self.items.append(OrderItem(product_id, quantity))
def confirm(self):
if not self.items:
raise EmptyOrder()
self.status = 'confirmed'
def cancel(self):
if self.status == 'shipped':
raise CannotCancelShippedOrder()
self.status = 'cancelled'
# Value Objects (неизменяемые объекты)
class Money:
def __init__(self, amount, currency):
self.amount = amount
self.currency = currency
def add(self, other):
if self.currency != other.currency:
raise CurrencyMismatch()
return Money(self.amount + other.amount, self.currency)
# Repositories (only for aggregates)
class OrderRepository:
def save(self, order):
# Persist entire aggregate
pass
# Domain Services (when logic spans multiple aggregates)
class PaymentService:
def charge_order(self, order: Order, payment: Payment):
# Business logic across domains
pass
# Bounded Contexts (разные домены приложения)
# Context 1: Orders domain
# Context 2: Payments domain
# Context 3: Shipping domain
# Они общаются через Events или APIs
Плюсы:
- Отражает реальный бизнес
- Легко обсуждать с business stakeholders
- Масштабируется хорошо
Минусы:
- Требует глубокого знания бизнеса
- Сложно для simple CRUD приложений
- Требует опыта
6. Event-Driven Architecture
Описание: компоненты общаются через события
┌──────────────┐
│ Order Service│ → Event: "OrderCreated"
└──────────────┘
│
↓ (Event Bus / Message Queue)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Payment │ │ Notification │ │ Analytics │
│ Service │ │ Service │ │ Service │
└──────────────┘ └──────────────┘ └──────────────┘
from kafka import KafkaProducer, KafkaConsumer
# Producer
class OrderService:
def create_order(self, items):
order = Order.create(items)
# Emit event
self.producer.send('order_created', {
'order_id': order.id,
'customer_id': order.customer_id,
'total': order.total,
'timestamp': datetime.now().isoformat()
})
return order
# Consumer
class PaymentService:
def on_order_created(self, event):
order_id = event['order_id']
total = event['total']
# Process payment
payment = self.process_payment(order_id, total)
# Emit next event
self.producer.send('payment_completed', {
'order_id': order_id,
'payment_id': payment.id
})
Плюсы:
- Слабая связанность (loose coupling)
- Асинхронная обработка
- Масштабируемость
Минусы:
- Сложно отлаживать
- Eventually consistent (не instant)
- Нужна инфраструктура (Kafka, RabbitMQ)
7. CQRS (Command Query Responsibility Segregation)
Описание: разделение на команды (write) и запросы (read)
# Command Model (Write)
class CreateOrderCommand:
def __init__(self, user_id, items):
self.user_id = user_id
self.items = items
class OrderCommandHandler:
def handle(self, command: CreateOrderCommand):
# Транзакция, консистентность
order = Order(user_id=command.user_id)
for item in command.items:
order.add_item(item)
self.repository.save(order)
return order
# Query Model (Read) — optimized for reading
class OrderReadModel:
def get_user_orders(self, user_id):
# Fast query from denormalized data
return cache.get(f'user:{user_id}:orders')
# Different databases
# Write: PostgreSQL (ACID)
# Read: Redis / Elasticsearch (fast)
Плюсы:
- Оптимизировать read и write отдельно
- Кеширование на write model
- Масштабировать reads независимо
Минусы:
- Consistency issues (eventual consistency)
- Complexity
Сравнение архитектур
| Архитектура | Размер | Complexity | Scalability | Team |
|---|---|---|---|---|
| Monolithic | Small | Low | Low | < 10 |
| Layered | Medium | Low | Medium | 10-30 |
| Clean | Medium | Medium | Medium | 10-30 |
| DDD | Large | High | High | 20+ |
| Microservices | Large | Very High | Very High | 30+ |
| Event-Driven | Any | High | Very High | DevOps needed |
| CQRS | Any | High | High | Tech experts |
Мой подход
На собеседовании:
- Я выбираю архитектуру в зависимости от требований
- Не религиозен к одному подходу
- Начинаю просто, потом усложняю
В реальности:
- MVP: Layered Monolithic (Django)
- 6 месяцев: Clean Architecture (still monolithic)
- 1 год: Event-Driven (если нужна масштабируемость)
- 2+ года: Микросервисы + DDD (если достаточно большая компания)
Golden Rule: Make it simple first. Add complexity only when you have the problem.