- Добавлены подробные принципы работы для всех архитектурных стилей - Расширен состав архитектуры с детальным описанием компонентов - Добавлены технологические решения и конкретные инструменты - Описаны паттерны интеграции и взаимодействия - Улучшена структура подачи информации Архитектурные стили: - Монолитная архитектура: принципы единого процесса, совместного развертывания - Микросервисная архитектура: декомпозиция, независимость, отказоустойчивость - Event-Driven Architecture: события как первоклассные объекты, асинхронность - CQRS: разделение команд и запросов, оптимизация моделей - Hexagonal Architecture: изоляция бизнес-логики, порты и адаптеры - Layered Architecture: разделение ответственности, направление зависимостей - Serverless Architecture: FaaS, event-driven execution, stateless functions Каждый стиль теперь содержит: - Подробные принципы работы (4 основных принципа) - Детальный состав архитектуры с компонентами - Технологические решения и инструменты - Паттерны интеграции и взаимодействия - Практические аспекты реализации
89 KiB
Архитектурные стили и паттерны
Навигация
Монолитная архитектура
Определение
Монолитная архитектура — это архитектурный стиль, при котором все компоненты приложения объединены в единое развертываемое приложение. Это традиционный подход к разработке программного обеспечения, где все функциональные возможности реализованы в рамках одного процесса.
Принципы работы
1. Единый процесс
- Все компоненты выполняются в рамках одного процесса операционной системы
- Общая память и ресурсы для всех модулей
- Простая отладка и профилирование
2. Совместное развертывание
- Все изменения развертываются одновременно
- Единый артефакт (JAR, WAR, EXE файл)
- Простота управления версиями
3. Прямое взаимодействие
- Компоненты вызывают друг друга через прямые ссылки
- Нет сетевых задержек между модулями
- Высокая производительность внутренних вызовов
4. Общие ресурсы
- Единая база данных для всех модулей
- Общие библиотеки и зависимости
- Централизованное управление конфигурацией
Состав архитектуры
Структурные компоненты:
- Презентационный слой — пользовательский интерфейс (UI/API)
- Бизнес-логика — основная логика приложения
- Слой данных — работа с базой данных
- Общие утилиты — вспомогательные функции
Технические компоненты:
- Веб-сервер — обработка HTTP-запросов
- Контейнер приложения — управление жизненным циклом
- ORM/DAO — работа с базой данных
- Кэш — хранение часто используемых данных
- Логирование — запись событий и ошибок
Инфраструктурные компоненты:
- База данных — хранение данных
- Файловая система — хранение файлов
- Внешние API — интеграция с внешними сервисами
- Мониторинг — отслеживание состояния приложения
Характеристики
- Единое развертывание — все компоненты развертываются вместе
- Общая база данных — все модули используют одну БД
- Единый технологический стек — один язык, один фреймворк
- Прямые вызовы — компоненты вызывают друг друга напрямую
Примеры реализации
🔧 Java (Spring Boot)
// Монолитное приложение e-commerce
@SpringBootApplication
public class EcommerceApplication {
public static void main(String[] args) {
SpringApplication.run(EcommerceApplication.class, args);
}
}
// Все контроллеры в одном приложении
@RestController
@RequestMapping("/api")
public class EcommerceController {
@Autowired
private UserService userService;
@Autowired
private OrderService orderService;
@Autowired
private PaymentService paymentService;
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) {
// Вся логика в одном месте
User user = userService.findById(request.getUserId());
Order order = orderService.createOrder(request);
PaymentResult payment = paymentService.processPayment(order);
return ResponseEntity.ok(order);
}
}
🦀 Rust (Actix Web)
// Монолитное приложение e-commerce
use actix_web::{web, App, HttpServer, Result};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct CreateOrderRequest {
pub user_id: u64,
pub items: Vec<OrderItem>,
}
#[derive(Serialize, Deserialize)]
pub struct Order {
pub id: u64,
pub user_id: u64,
pub status: String,
pub total: f64,
}
// Все сервисы в одном приложении
async fn create_order(
order_data: web::Json<CreateOrderRequest>,
user_service: web::Data<UserService>,
order_service: web::Data<OrderService>,
payment_service: web::Data<PaymentService>,
) -> Result<web::Json<Order>> {
// Вся логика в одном месте
let user = user_service.find_by_id(order_data.user_id).await?;
let order = order_service.create_order(order_data.into_inner()).await?;
let _payment = payment_service.process_payment(&order).await?;
Ok(web::Json(order))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(web::resource("/api/orders").route(web::post().to(create_order)))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
🟨 JavaScript (Node.js)
// Монолитное приложение e-commerce
const express = require('express');
const app = express();
// Все сервисы в одном приложении
class EcommerceService {
constructor() {
this.userService = new UserService();
this.orderService = new OrderService();
this.paymentService = new PaymentService();
}
async createOrder(request) {
// Вся логика в одном месте
const user = await this.userService.findById(request.userId);
const order = await this.orderService.createOrder(request);
const payment = await this.paymentService.processPayment(order);
return order;
}
}
app.post('/api/orders', async (req, res) => {
try {
const ecommerceService = new EcommerceService();
const order = await ecommerceService.createOrder(req.body);
res.json(order);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => {
console.log('Monolithic e-commerce app running on port 3000');
});
🐍 Python (FastAPI)
# Монолитное приложение e-commerce
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List
app = FastAPI()
class CreateOrderRequest(BaseModel):
user_id: int
items: List[dict]
class Order(BaseModel):
id: int
user_id: int
status: str
total: float
# Все сервисы в одном приложении
class EcommerceService:
def __init__(self):
self.user_service = UserService()
self.order_service = OrderService()
self.payment_service = PaymentService()
async def create_order(self, request: CreateOrderRequest) -> Order:
# Вся логика в одном месте
user = await self.user_service.find_by_id(request.user_id)
order = await self.order_service.create_order(request)
payment = await self.payment_service.process_payment(order)
return order
@app.post("/api/orders")
async def create_order(request: CreateOrderRequest):
try:
ecommerce_service = EcommerceService()
order = await ecommerce_service.create_order(request)
return order
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Преимущества
- Простота разработки — легко начать проект
- Простота тестирования — все в одном процессе
- Простота развертывания — один артефакт
- Производительность — нет сетевых вызовов
Недостатки
- Сложность масштабирования — нужно масштабировать все приложение
- Технологические ограничения — привязанность к одному стеку
- Риск сбоев — падение одного компонента влияет на все
- Сложность команд — команды работают над одним кодом
Микросервисная архитектура
Определение
Микросервисная архитектура — это архитектурный стиль, при котором приложение разбивается на множество небольших, независимых сервисов. Каждый сервис отвечает за определенную бизнес-функцию и может быть разработан, развернут и масштабирован независимо от других сервисов.
Принципы работы
1. Декомпозиция по бизнес-функциям
- Каждый сервис отвечает за одну бизнес-функцию
- Сервисы организованы вокруг доменных границ
- Минимизация связанности между сервисами
2. Независимость сервисов
- Каждый сервис имеет собственный жизненный цикл
- Независимое развертывание и масштабирование
- Собственная база данных и технологии
3. Децентрализованное управление
- Каждая команда отвечает за свои сервисы
- Разные технологии и подходы к разработке
- Децентрализованные данные и управление
4. Отказоустойчивость
- Сервисы должны быть устойчивы к сбоям других сервисов
- Circuit Breaker и retry механизмы
- Graceful degradation при недоступности зависимостей
Состав архитектуры
Основные компоненты:
- Микросервисы — независимые сервисы с собственной логикой
- API Gateway — единая точка входа для клиентов
- Service Discovery — механизм поиска и регистрации сервисов
- Load Balancer — распределение нагрузки между экземплярами
Инфраструктурные компоненты:
- Контейнерная платформа — Docker, Kubernetes
- Оркестратор — управление жизненным циклом сервисов
- Мониторинг — отслеживание состояния сервисов
- Логирование — централизованный сбор логов
Паттерны взаимодействия:
- Синхронное взаимодействие — REST, GraphQL, gRPC
- Асинхронное взаимодействие — Message Queues, Event Streaming
- Saga Pattern — управление распределенными транзакциями
- CQRS — разделение команд и запросов
Качество и надежность:
- Circuit Breaker — защита от каскадных сбоев
- Bulkhead — изоляция ресурсов
- Timeout — ограничение времени выполнения
- Retry — повторные попытки при сбоях
Характеристики
- Независимые сервисы — каждый сервис работает автономно
- Отдельные базы данных — каждый сервис имеет свою БД
- Разные технологии — сервисы могут использовать разные стеки
- Сетевое взаимодействие — сервисы общаются по сети
Примеры реализации
🔧 Java (Spring Boot)
// User Service
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}
}
// Order Service
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private UserServiceClient userServiceClient;
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) {
// Вызов внешнего сервиса
User user = userServiceClient.getUser(request.getUserId());
Order order = orderService.createOrder(request);
return ResponseEntity.ok(order);
}
}
// Payment Service
@RestController
@RequestMapping("/api/payments")
public class PaymentController {
@Autowired
private PaymentService paymentService;
@PostMapping
public ResponseEntity<PaymentResult> processPayment(@RequestBody PaymentRequest request) {
PaymentResult result = paymentService.processPayment(request);
return ResponseEntity.ok(result);
}
}
🦀 Rust (Actix Web)
// User Service
use actix_web::{web, App, HttpServer, Result};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct User {
pub id: u64,
pub name: String,
pub email: String,
}
// User Service
async fn get_user(path: web::Path<u64>) -> Result<web::Json<User>> {
let user_id = path.into_inner();
let user = user_service.find_by_id(user_id).await?;
Ok(web::Json(user))
}
// Order Service
async fn create_order(
order_data: web::Json<CreateOrderRequest>,
user_client: web::Data<UserServiceClient>,
) -> Result<web::Json<Order>> {
// Вызов внешнего сервиса
let user = user_client.get_user(order_data.user_id).await?;
let order = order_service.create_order(order_data.into_inner()).await?;
Ok(web::Json(order))
}
// Payment Service
async fn process_payment(
payment_data: web::Json<PaymentRequest>,
) -> Result<web::Json<PaymentResult>> {
let result = payment_service.process_payment(payment_data.into_inner()).await?;
Ok(web::Json(result))
}
🟨 JavaScript (Node.js)
// User Service
const express = require('express');
const app = express();
class UserService {
async findById(id) {
// Логика получения пользователя
return { id, name: 'John Doe', email: 'john@example.com' };
}
}
app.get('/api/users/:id', async (req, res) => {
try {
const userService = new UserService();
const user = await userService.findById(req.params.id);
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Order Service
class OrderService {
constructor(userServiceClient) {
this.userServiceClient = userServiceClient;
}
async createOrder(request) {
// Вызов внешнего сервиса
const user = await this.userServiceClient.getUser(request.userId);
const order = { id: Date.now(), userId: user.id, status: 'created' };
return order;
}
}
app.post('/api/orders', async (req, res) => {
try {
const userServiceClient = new UserServiceClient();
const orderService = new OrderService(userServiceClient);
const order = await orderService.createOrder(req.body);
res.json(order);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
🐍 Python (FastAPI)
# User Service
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
name: str
email: str
class UserService:
async def find_by_id(self, user_id: int) -> User:
# Логика получения пользователя
return User(id=user_id, name="John Doe", email="john@example.com")
@app.get("/api/users/{user_id}")
async def get_user(user_id: int):
try:
user_service = UserService()
user = await user_service.find_by_id(user_id)
return user
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# Order Service
class OrderService:
def __init__(self, user_service_client):
self.user_service_client = user_service_client
async def create_order(self, request):
# Вызов внешнего сервиса
user = await self.user_service_client.get_user(request.user_id)
order = {"id": 123, "user_id": user.id, "status": "created"}
return order
@app.post("/api/orders")
async def create_order(request: CreateOrderRequest):
try:
user_service_client = UserServiceClient()
order_service = OrderService(user_service_client)
order = await order_service.create_order(request)
return order
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Преимущества
- Независимое масштабирование — каждый сервис масштабируется отдельно
- Технологическое разнообразие — разные технологии для разных задач
- Независимые команды — команды работают над разными сервисами
- Отказоустойчивость — падение одного сервиса не влияет на другие
Недостатки
- Сложность разработки — нужно управлять множеством сервисов
- Сетевая задержка — вызовы между сервисами медленнее
- Сложность тестирования — нужно тестировать интеграции
- Сложность развертывания — нужно координировать развертывание
Event-Driven Architecture (EDA)
Определение
Event-Driven Architecture — это архитектурный стиль, основанный на производстве, обнаружении, потреблении и реакции на события. Система реагирует на события, которые происходят в реальном времени, обеспечивая слабую связанность и высокую отзывчивость.
Принципы работы
1. События как первоклассные объекты
- События представляют факты, которые произошли в системе
- События неизменяемы и содержат всю необходимую информацию
- События имеют временную метку и уникальный идентификатор
2. Асинхронная обработка
- События обрабатываются асинхронно
- Отправитель не ждет обработки события получателем
- Высокая производительность и отзывчивость системы
3. Слабая связанность
- Компоненты не знают друг о друге напрямую
- Взаимодействие происходит только через события
- Легкость изменения и расширения системы
4. Eventual Consistency
- Система стремится к консистентности в конечном итоге
- Временная несогласованность допустима
- Компенсирующие действия для восстановления консистентности
Состав архитектуры
Основные компоненты:
- Event Producers — компоненты, генерирующие события
- Event Store — хранилище событий (Event Sourcing)
- Event Bus/Message Broker — транспорт для событий
- Event Consumers — компоненты, обрабатывающие события
Паттерны обработки событий:
- Simple Event Processing — прямая обработка событий
- Stream Processing — обработка потоков событий
- Complex Event Processing — анализ сложных паттернов событий
- Event Sourcing — хранение состояния как последовательности событий
Технологические компоненты:
- Message Brokers — Apache Kafka, RabbitMQ, AWS SQS
- Event Streaming — Apache Kafka Streams, Apache Pulsar
- Event Store — EventStore, Apache Kafka
- Processing Engines — Apache Storm, Apache Flink
Паттерны интеграции:
- Event Notification — уведомление о произошедших событиях
- Event-Carried State Transfer — передача состояния через события
- Event Sourcing — восстановление состояния из событий
- CQRS — разделение команд и запросов с событиями
Характеристики
- События — значимые изменения в системе
- Асинхронность — события обрабатываются асинхронно
- Слабая связанность — компоненты не знают друг о друге
- Масштабируемость — легко добавлять новых потребителей
Примеры реализации
🔧 Java (Spring Boot)
// Доменное событие
public class OrderCreatedEvent {
private final String orderId;
private final String customerId;
private final BigDecimal amount;
private final LocalDateTime occurredOn;
public OrderCreatedEvent(String orderId, String customerId, BigDecimal amount) {
this.orderId = orderId;
this.customerId = customerId;
this.amount = amount;
this.occurredOn = LocalDateTime.now();
}
// Геттеры
}
// Публикация события
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public Order createOrder(CreateOrderRequest request) {
Order order = new Order(request);
order = orderRepository.save(order);
// Публикация события
eventPublisher.publishEvent(new OrderCreatedEvent(
order.getId(),
order.getCustomerId(),
order.getAmount()
));
return order;
}
}
// Обработка события
@Component
public class OrderCreatedEventHandler {
@Autowired
private InventoryService inventoryService;
@Autowired
private NotificationService notificationService;
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// Резервирование товара
inventoryService.reserveItems(event.getOrderId());
// Отправка уведомления
notificationService.sendOrderConfirmation(event.getCustomerId());
}
}
🦀 Rust (Actix Web + Tokio)
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
use std::collections::HashMap;
// Доменное событие
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderCreatedEvent {
pub order_id: String,
pub customer_id: String,
pub amount: f64,
pub occurred_on: chrono::DateTime<chrono::Utc>,
}
// Event Publisher
pub struct EventPublisher {
sender: mpsc::UnboundedSender<OrderCreatedEvent>,
}
impl EventPublisher {
pub fn new(sender: mpsc::UnboundedSender<OrderCreatedEvent>) -> Self {
Self { sender }
}
pub fn publish_event(&self, event: OrderCreatedEvent) -> Result<(), String> {
self.sender.send(event).map_err(|_| "Failed to send event".to_string())
}
}
// Order Service
pub struct OrderService {
event_publisher: EventPublisher,
}
impl OrderService {
pub async fn create_order(&self, request: CreateOrderRequest) -> Result<Order, String> {
let order = Order::new(request);
// Сохранение заказа
// Публикация события
let event = OrderCreatedEvent {
order_id: order.id.clone(),
customer_id: order.customer_id.clone(),
amount: order.amount,
occurred_on: chrono::Utc::now(),
};
self.event_publisher.publish_event(event)?;
Ok(order)
}
}
// Event Handler
pub struct OrderCreatedEventHandler {
inventory_service: InventoryService,
notification_service: NotificationService,
}
impl OrderCreatedEventHandler {
pub async fn handle_order_created(&self, event: OrderCreatedEvent) {
// Резервирование товара
self.inventory_service.reserve_items(&event.order_id).await;
// Отправка уведомления
self.notification_service.send_order_confirmation(&event.customer_id).await;
}
}
🟨 JavaScript (Node.js + EventEmitter)
const EventEmitter = require('events');
// Доменное событие
class OrderCreatedEvent {
constructor(orderId, customerId, amount) {
this.orderId = orderId;
this.customerId = customerId;
this.amount = amount;
this.occurredOn = new Date();
}
}
// Event Publisher
class EventPublisher extends EventEmitter {
publishEvent(event) {
this.emit('orderCreated', event);
}
}
// Order Service
class OrderService {
constructor(eventPublisher) {
this.eventPublisher = eventPublisher;
}
async createOrder(request) {
const order = new Order(request);
// Сохранение заказа
// Публикация события
const event = new OrderCreatedEvent(
order.id,
order.customerId,
order.amount
);
this.eventPublisher.publishEvent(event);
return order;
}
}
// Event Handler
class OrderCreatedEventHandler {
constructor(inventoryService, notificationService) {
this.inventoryService = inventoryService;
this.notificationService = notificationService;
}
handleOrderCreated(event) {
// Резервирование товара
this.inventoryService.reserveItems(event.orderId);
// Отправка уведомления
this.notificationService.sendOrderConfirmation(event.customerId);
}
}
// Настройка обработчиков
const eventPublisher = new EventPublisher();
const orderService = new OrderService(eventPublisher);
const eventHandler = new OrderCreatedEventHandler(
new InventoryService(),
new NotificationService()
);
eventPublisher.on('orderCreated', (event) => {
eventHandler.handleOrderCreated(event);
});
🐍 Python (FastAPI + asyncio)
import asyncio
from datetime import datetime
from typing import Dict, Any
from dataclasses import dataclass
# Доменное событие
@dataclass
class OrderCreatedEvent:
order_id: str
customer_id: str
amount: float
occurred_on: datetime
# Event Publisher
class EventPublisher:
def __init__(self):
self._handlers = []
def subscribe(self, handler):
self._handlers.append(handler)
async def publish_event(self, event: OrderCreatedEvent):
for handler in self._handlers:
await handler(event)
# Order Service
class OrderService:
def __init__(self, event_publisher: EventPublisher):
self.event_publisher = event_publisher
async def create_order(self, request):
order = Order(request)
# Сохранение заказа
# Публикация события
event = OrderCreatedEvent(
order_id=order.id,
customer_id=order.customer_id,
amount=order.amount,
occurred_on=datetime.now()
)
await self.event_publisher.publish_event(event)
return order
# Event Handler
class OrderCreatedEventHandler:
def __init__(self, inventory_service, notification_service):
self.inventory_service = inventory_service
self.notification_service = notification_service
async def __call__(self, event: OrderCreatedEvent):
# Резервирование товара
await self.inventory_service.reserve_items(event.order_id)
# Отправка уведомления
await self.notification_service.send_order_confirmation(event.customer_id)
# Настройка
event_publisher = EventPublisher()
order_service = OrderService(event_publisher)
event_handler = OrderCreatedEventHandler(
InventoryService(),
NotificationService()
)
event_publisher.subscribe(event_handler)
CQRS (Command Query Responsibility Segregation)
Определение
CQRS — это паттерн, который разделяет операции чтения и записи данных на разные модели. Это позволяет оптимизировать каждую сторону независимо, обеспечивая лучшую производительность и масштабируемость.
Принципы работы
1. Разделение ответственности
- Commands — операции изменения состояния (Create, Update, Delete)
- Queries — операции чтения данных (Read, Search, Report)
- Разные модели для разных операций
- Специализированные обработчики для каждого типа
2. Оптимизация моделей
- Write Model — нормализованная структура для консистентности
- Read Model — денормализованная структура для производительности
- Разные схемы баз данных для чтения и записи
- Специализированные индексы и структуры данных
3. Асинхронная синхронизация
- Изменения в Write Model асинхронно синхронизируются с Read Model
- Event-driven обновление Read Model
- Eventual consistency между моделями
- Компенсирующие действия при сбоях синхронизации
4. Независимое масштабирование
- Write и Read стороны масштабируются независимо
- Разные технологии для разных задач
- Оптимизация ресурсов под конкретные нагрузки
- Горизонтальное масштабирование каждой стороны
Состав архитектуры
Основные компоненты:
- Command Side — обработка команд изменения данных
- Query Side — обработка запросов чтения данных
- Event Bus — синхронизация между сторонами
- Projection Handlers — обновление Read Model
Command Side компоненты:
- Command Handlers — обработчики команд
- Domain Models — бизнес-логика и валидация
- Write Database — основное хранилище данных
- Event Store — хранение событий изменений
Query Side компоненты:
- Query Handlers — обработчики запросов
- Read Models — оптимизированные модели для чтения
- Read Database — специализированные хранилища
- Caching Layer — кэширование часто запрашиваемых данных
Синхронизация:
- Event Publishers — публикация событий изменений
- Event Subscribers — подписка на события
- Projection Services — обновление Read Model
- Reconciliation — восстановление консистентности
Технологические решения:
- Message Brokers — Apache Kafka, RabbitMQ для событий
- Databases — PostgreSQL для Write, MongoDB для Read
- Caching — Redis для кэширования Read Model
- Search Engines — Elasticsearch для сложных запросов
Характеристики
- Разделение команд и запросов — разные модели для записи и чтения
- Оптимизация — модели оптимизированы для своих задач
- Масштабируемость — можно масштабировать чтение и запись отдельно
- Сложность — увеличивает сложность системы
Примеры реализации
🔧 Java (Spring Boot)
// Command Model (Write)
@Entity
@Table(name = "orders")
public class Order {
@Id
private String id;
private String customerId;
private OrderStatus status;
private BigDecimal amount;
private LocalDateTime createdAt;
// Методы для изменения состояния
public void confirm() {
this.status = OrderStatus.CONFIRMED;
}
public void cancel() {
this.status = OrderStatus.CANCELLED;
}
}
// Query Model (Read)
@Entity
@Table(name = "order_summaries")
public class OrderSummary {
@Id
private String id;
private String customerName;
private String customerEmail;
private OrderStatus status;
private BigDecimal amount;
private LocalDateTime createdAt;
private int itemCount;
// Только геттеры для чтения
}
// Command Handler
@Service
public class OrderCommandHandler {
@Autowired
private OrderRepository orderRepository;
@Autowired
private OrderSummaryRepository orderSummaryRepository;
public void handle(CreateOrderCommand command) {
// Создание заказа
Order order = new Order(command);
orderRepository.save(order);
// Обновление read model
OrderSummary summary = new OrderSummary(order);
orderSummaryRepository.save(summary);
}
}
// Query Handler
@Service
public class OrderQueryHandler {
@Autowired
private OrderSummaryRepository orderSummaryRepository;
public List<OrderSummary> handle(GetOrdersQuery query) {
return orderSummaryRepository.findByCustomerId(query.getCustomerId());
}
}
🦀 Rust (Actix Web + Diesel)
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
// Command Model (Write)
#[derive(Insertable, Queryable, Serialize, Deserialize)]
#[table_name = "orders"]
pub struct Order {
pub id: String,
pub customer_id: String,
pub status: String,
pub amount: f64,
pub created_at: chrono::DateTime<chrono::Utc>,
}
impl Order {
pub fn confirm(&mut self) {
self.status = "CONFIRMED".to_string();
}
pub fn cancel(&mut self) {
self.status = "CANCELLED".to_string();
}
}
// Query Model (Read)
#[derive(Queryable, Serialize, Deserialize)]
#[table_name = "order_summaries"]
pub struct OrderSummary {
pub id: String,
pub customer_name: String,
pub customer_email: String,
pub status: String,
pub amount: f64,
pub created_at: chrono::DateTime<chrono::Utc>,
pub item_count: i32,
}
// Command Handler
pub struct OrderCommandHandler {
order_repository: OrderRepository,
order_summary_repository: OrderSummaryRepository,
}
impl OrderCommandHandler {
pub async fn handle(&self, command: CreateOrderCommand) -> Result<(), String> {
// Создание заказа
let order = Order::new(command);
self.order_repository.save(order.clone()).await?;
// Обновление read model
let summary = OrderSummary::from_order(order);
self.order_summary_repository.save(summary).await?;
Ok(())
}
}
// Query Handler
pub struct OrderQueryHandler {
order_summary_repository: OrderSummaryRepository,
}
impl OrderQueryHandler {
pub async fn handle(&self, query: GetOrdersQuery) -> Result<Vec<OrderSummary>, String> {
self.order_summary_repository.find_by_customer_id(&query.customer_id).await
}
}
🟨 JavaScript (Node.js + MongoDB)
// Command Model (Write)
class Order {
constructor(data) {
this.id = data.id;
this.customerId = data.customerId;
this.status = data.status;
this.amount = data.amount;
this.createdAt = new Date();
}
confirm() {
this.status = 'CONFIRMED';
}
cancel() {
this.status = 'CANCELLED';
}
}
// Query Model (Read)
class OrderSummary {
constructor(order) {
this.id = order.id;
this.customerName = order.customerName;
this.customerEmail = order.customerEmail;
this.status = order.status;
this.amount = order.amount;
this.createdAt = order.createdAt;
this.itemCount = order.items.length;
}
}
// Command Handler
class OrderCommandHandler {
constructor(orderRepository, orderSummaryRepository) {
this.orderRepository = orderRepository;
this.orderSummaryRepository = orderSummaryRepository;
}
async handle(command) {
// Создание заказа
const order = new Order(command);
await this.orderRepository.save(order);
// Обновление read model
const summary = new OrderSummary(order);
await this.orderSummaryRepository.save(summary);
}
}
// Query Handler
class OrderQueryHandler {
constructor(orderSummaryRepository) {
this.orderSummaryRepository = orderSummaryRepository;
}
async handle(query) {
return await this.orderSummaryRepository.findByCustomerId(query.customerId);
}
}
🐍 Python (FastAPI + SQLAlchemy)
from sqlalchemy import Column, String, Float, DateTime, Integer
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
Base = declarative_base()
# Command Model (Write)
class Order(Base):
__tablename__ = "orders"
id = Column(String, primary_key=True)
customer_id = Column(String, nullable=False)
status = Column(String, nullable=False)
amount = Column(Float, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
def confirm(self):
self.status = "CONFIRMED"
def cancel(self):
self.status = "CANCELLED"
# Query Model (Read)
class OrderSummary(Base):
__tablename__ = "order_summaries"
id = Column(String, primary_key=True)
customer_name = Column(String, nullable=False)
customer_email = Column(String, nullable=False)
status = Column(String, nullable=False)
amount = Column(Float, nullable=False)
created_at = Column(DateTime, nullable=False)
item_count = Column(Integer, nullable=False)
# Command Handler
class OrderCommandHandler:
def __init__(self, order_repository, order_summary_repository):
self.order_repository = order_repository
self.order_summary_repository = order_summary_repository
async def handle(self, command):
# Создание заказа
order = Order(command)
await self.order_repository.save(order)
# Обновление read model
summary = OrderSummary.from_order(order)
await self.order_summary_repository.save(summary)
# Query Handler
class OrderQueryHandler:
def __init__(self, order_summary_repository):
self.order_summary_repository = order_summary_repository
async def handle(self, query):
return await self.order_summary_repository.find_by_customer_id(query.customer_id)
Hexagonal Architecture (Ports and Adapters)
Определение
Hexagonal Architecture — это архитектурный паттерн, который изолирует бизнес-логику от внешних зависимостей. Также известен как "Ports and Adapters" или "Clean Architecture", он обеспечивает независимость бизнес-логики от внешних технологий и инфраструктуры.
Принципы работы
1. Изоляция бизнес-логики
- Бизнес-логика находится в центре архитектуры
- Не зависит от внешних технологий и фреймворков
- Чистые доменные модели без внешних зависимостей
- Легкость тестирования и изменения
2. Порты и адаптеры
- Порты — интерфейсы, определяющие контракты взаимодействия
- Адаптеры — реализации портов для конкретных технологий
- Инверсия зависимостей — бизнес-логика определяет интерфейсы
- Внешние компоненты реализуют эти интерфейсы
3. Направление зависимостей
- Зависимости направлены внутрь, к бизнес-логике
- Внешние слои зависят от внутренних
- Бизнес-логика не знает о внешних технологиях
- Легкость замены внешних компонентов
4. Тестируемость
- Бизнес-логика тестируется изолированно
- Mock-объекты для внешних зависимостей
- Unit-тесты без внешних ресурсов
- Интеграционные тесты с реальными адаптерами
Состав архитектуры
Структурные слои:
- Domain Layer — бизнес-логика и доменные модели
- Application Layer — координация бизнес-операций
- Infrastructure Layer — внешние зависимости и технологии
- Presentation Layer — пользовательский интерфейс
Domain Layer компоненты:
- Entities — основные бизнес-объекты
- Value Objects — неизменяемые объекты
- Domain Services — бизнес-логика, не принадлежащая сущностям
- Domain Events — события домена
- Repositories (Ports) — интерфейсы для работы с данными
Application Layer компоненты:
- Use Cases — координация бизнес-операций
- Application Services — оркестрация доменных объектов
- Command/Query Handlers — обработка команд и запросов
- Ports — интерфейсы для внешних сервисов
Infrastructure Layer компоненты:
- Repository Implementations — реализация репозиториев
- External Service Adapters — адаптеры для внешних API
- Database Adapters — работа с базами данных
- Message Queue Adapters — работа с очередями сообщений
Presentation Layer компоненты:
- Controllers — обработка HTTP-запросов
- View Models — модели представления
- API Adapters — адаптеры для внешних API
- UI Components — компоненты пользовательского интерфейса
Паттерны интеграции:
- Dependency Injection — внедрение зависимостей
- Factory Pattern — создание объектов
- Observer Pattern — обработка событий
- Strategy Pattern — выбор алгоритмов
Характеристики
- Порты — интерфейсы для взаимодействия с внешним миром
- Адаптеры — реализации портов для конкретных технологий
- Изоляция — бизнес-логика не зависит от внешних технологий
- Тестируемость — легко тестировать с mock-объектами
Примеры реализации
🔧 Java (Spring Boot)
// Domain Layer (внутренний круг)
public class Order {
private String id;
private String customerId;
private OrderStatus status;
private BigDecimal amount;
public void confirm() {
this.status = OrderStatus.CONFIRMED;
}
}
// Ports (интерфейсы)
public interface OrderRepository {
void save(Order order);
Optional<Order> findById(String id);
}
public interface PaymentService {
PaymentResult processPayment(PaymentRequest request);
}
public interface NotificationService {
void sendNotification(String recipient, String message);
}
// Application Layer
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentService paymentService;
private final NotificationService notificationService;
public OrderService(OrderRepository orderRepository,
PaymentService paymentService,
NotificationService notificationService) {
this.orderRepository = orderRepository;
this.paymentService = paymentService;
this.notificationService = notificationService;
}
public void processOrder(Order order) {
// Бизнес-логика
order.confirm();
orderRepository.save(order);
// Внешние сервисы через порты
PaymentResult payment = paymentService.processPayment(
new PaymentRequest(order.getAmount())
);
if (payment.isSuccess()) {
notificationService.sendNotification(
order.getCustomerId(),
"Order confirmed"
);
}
}
}
// Adapters (внешний круг)
@Repository
public class JpaOrderRepository implements OrderRepository {
@Override
public void save(Order order) {
// JPA реализация
}
@Override
public Optional<Order> findById(String id) {
// JPA реализация
return Optional.empty();
}
}
@Component
public class StripePaymentService implements PaymentService {
@Override
public PaymentResult processPayment(PaymentRequest request) {
// Stripe API реализация
return new PaymentResult(true, "Payment processed");
}
}
@Component
public class EmailNotificationService implements NotificationService {
@Override
public void sendNotification(String recipient, String message) {
// Email реализация
}
}
🦀 Rust (Actix Web + Dependency Injection)
use std::sync::Arc;
use async_trait::async_trait;
// Domain Layer (внутренний круг)
#[derive(Debug, Clone)]
pub struct Order {
pub id: String,
pub customer_id: String,
pub status: String,
pub amount: f64,
}
impl Order {
pub fn confirm(&mut self) {
self.status = "CONFIRMED".to_string();
}
}
// Ports (интерфейсы)
#[async_trait]
pub trait OrderRepository: Send + Sync {
async fn save(&self, order: &Order) -> Result<(), String>;
async fn find_by_id(&self, id: &str) -> Result<Option<Order>, String>;
}
#[async_trait]
pub trait PaymentService: Send + Sync {
async fn process_payment(&self, request: PaymentRequest) -> Result<PaymentResult, String>;
}
#[async_trait]
pub trait NotificationService: Send + Sync {
async fn send_notification(&self, recipient: &str, message: &str) -> Result<(), String>;
}
// Application Layer
pub struct OrderService {
order_repository: Arc<dyn OrderRepository>,
payment_service: Arc<dyn PaymentService>,
notification_service: Arc<dyn NotificationService>,
}
impl OrderService {
pub fn new(
order_repository: Arc<dyn OrderRepository>,
payment_service: Arc<dyn PaymentService>,
notification_service: Arc<dyn NotificationService>,
) -> Self {
Self {
order_repository,
payment_service,
notification_service,
}
}
pub async fn process_order(&self, mut order: Order) -> Result<(), String> {
// Бизнес-логика
order.confirm();
self.order_repository.save(&order).await?;
// Внешние сервисы через порты
let payment = self.payment_service.process_payment(
PaymentRequest::new(order.amount)
).await?;
if payment.is_success {
self.notification_service.send_notification(
&order.customer_id,
"Order confirmed"
).await?;
}
Ok(())
}
}
// Adapters (внешний круг)
pub struct PostgresOrderRepository {
pool: sqlx::PgPool,
}
#[async_trait]
impl OrderRepository for PostgresOrderRepository {
async fn save(&self, order: &Order) -> Result<(), String> {
sqlx::query!(
"INSERT INTO orders (id, customer_id, status, amount) VALUES ($1, $2, $3, $4)",
order.id, order.customer_id, order.status, order.amount
)
.execute(&self.pool)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
async fn find_by_id(&self, id: &str) -> Result<Option<Order>, String> {
let row = sqlx::query!(
"SELECT id, customer_id, status, amount FROM orders WHERE id = $1",
id
)
.fetch_optional(&self.pool)
.await
.map_err(|e| e.to_string())?;
Ok(row.map(|r| Order {
id: r.id,
customer_id: r.customer_id,
status: r.status,
amount: r.amount,
}))
}
}
🟨 JavaScript (Node.js + Dependency Injection)
// Domain Layer (внутренний круг)
class Order {
constructor(data) {
this.id = data.id;
this.customerId = data.customerId;
this.status = data.status;
this.amount = data.amount;
}
confirm() {
this.status = 'CONFIRMED';
}
}
// Ports (интерфейсы)
class OrderRepository {
async save(order) {
throw new Error('Not implemented');
}
async findById(id) {
throw new Error('Not implemented');
}
}
class PaymentService {
async processPayment(request) {
throw new Error('Not implemented');
}
}
class NotificationService {
async sendNotification(recipient, message) {
throw new Error('Not implemented');
}
}
// Application Layer
class OrderService {
constructor(orderRepository, paymentService, notificationService) {
this.orderRepository = orderRepository;
this.paymentService = paymentService;
this.notificationService = notificationService;
}
async processOrder(order) {
// Бизнес-логика
order.confirm();
await this.orderRepository.save(order);
// Внешние сервисы через порты
const payment = await this.paymentService.processPayment({
amount: order.amount
});
if (payment.isSuccess) {
await this.notificationService.sendNotification(
order.customerId,
'Order confirmed'
);
}
}
}
// Adapters (внешний круг)
class MongoOrderRepository extends OrderRepository {
constructor(mongoClient) {
super();
this.mongoClient = mongoClient;
}
async save(order) {
const db = this.mongoClient.db('ecommerce');
await db.collection('orders').replaceOne(
{ id: order.id },
order,
{ upsert: true }
);
}
async findById(id) {
const db = this.mongoClient.db('ecommerce');
const order = await db.collection('orders').findOne({ id });
return order ? new Order(order) : null;
}
}
class StripePaymentService extends PaymentService {
constructor(stripeClient) {
super();
this.stripeClient = stripeClient;
}
async processPayment(request) {
// Интеграция со Stripe
const paymentIntent = await this.stripeClient.paymentIntents.create({
amount: request.amount * 100,
currency: 'usd',
});
return {
isSuccess: paymentIntent.status === 'succeeded',
message: 'Payment successful'
};
}
}
class EmailNotificationService extends NotificationService {
constructor(emailClient) {
super();
this.emailClient = emailClient;
}
async sendNotification(recipient, message) {
await this.emailClient.send({
to: recipient,
subject: 'Order Update',
text: message
});
}
}
🐍 Python (FastAPI + Dependency Injection)
from abc import ABC, abstractmethod
from typing import Optional
from dataclasses import dataclass
# Domain Layer (внутренний круг)
@dataclass
class Order:
id: str
customer_id: str
status: str
amount: float
def confirm(self):
self.status = "CONFIRMED"
# Ports (интерфейсы)
class OrderRepository(ABC):
@abstractmethod
async def save(self, order: Order) -> None:
pass
@abstractmethod
async def find_by_id(self, order_id: str) -> Optional[Order]:
pass
class PaymentService(ABC):
@abstractmethod
async def process_payment(self, request: PaymentRequest) -> PaymentResult:
pass
class NotificationService(ABC):
@abstractmethod
async def send_notification(self, recipient: str, message: str) -> None:
pass
# Application Layer
class OrderService:
def __init__(
self,
order_repository: OrderRepository,
payment_service: PaymentService,
notification_service: NotificationService
):
self.order_repository = order_repository
self.payment_service = payment_service
self.notification_service = notification_service
async def process_order(self, order: Order) -> None:
# Бизнес-логика
order.confirm()
await self.order_repository.save(order)
# Внешние сервисы через порты
payment = await self.payment_service.process_payment(
PaymentRequest(amount=order.amount)
)
if payment.is_success:
await self.notification_service.send_notification(
order.customer_id,
"Order confirmed"
)
# Adapters (внешний круг)
class PostgresOrderRepository(OrderRepository):
def __init__(self, db_pool):
self.db_pool = db_pool
async def save(self, order: Order) -> None:
async with self.db_pool.acquire() as conn:
await conn.execute(
"INSERT INTO orders (id, customer_id, status, amount) VALUES ($1, $2, $3, $4) "
"ON CONFLICT (id) DO UPDATE SET customer_id = $2, status = $3, amount = $4",
order.id, order.customer_id, order.status, order.amount
)
async def find_by_id(self, order_id: str) -> Optional[Order]:
async with self.db_pool.acquire() as conn:
row = await conn.fetchrow(
"SELECT id, customer_id, status, amount FROM orders WHERE id = $1",
order_id
)
if row:
return Order(
id=row['id'],
customer_id=row['customer_id'],
status=row['status'],
amount=row['amount']
)
return None
class StripePaymentService(PaymentService):
def __init__(self, stripe_client):
self.stripe_client = stripe_client
async def process_payment(self, request: PaymentRequest) -> PaymentResult:
# Интеграция со Stripe
payment_intent = await self.stripe_client.payment_intents.create(
amount=int(request.amount * 100),
currency='usd'
)
return PaymentResult(
is_success=payment_intent.status == 'succeeded',
message='Payment successful'
)
class EmailNotificationService(NotificationService):
def __init__(self, email_client):
self.email_client = email_client
async def send_notification(self, recipient: str, message: str) -> None:
await self.email_client.send_email(
to=recipient,
subject='Order Update',
body=message
)
Layered Architecture
Определение
Layered Architecture — это архитектурный паттерн, который организует код в горизонтальные слои. Каждый слой имеет определенную ответственность и может взаимодействовать только с соседними слоями, обеспечивая четкое разделение обязанностей и управляемость системы.
Принципы работы
1. Разделение ответственности
- Каждый слой имеет четко определенную ответственность
- Слои изолированы друг от друга
- Изменения в одном слое не влияют на другие
- Легкость понимания и поддержки кода
2. Направление зависимостей
- Зависимости направлены вниз (к более низким слоям)
- Высокие слои зависят от низких
- Низкие слои не знают о высоких
- Инверсия зависимостей через интерфейсы
3. Инкапсуляция
- Детали реализации скрыты внутри слоя
- Внешние слои взаимодействуют через интерфейсы
- Изменения в реализации не влияют на клиентов
- Легкость замены компонентов
4. Повторное использование
- Низкие слои могут использоваться разными высокими слоями
- Общая функциональность выносится в отдельные слои
- Компоненты могут быть переиспользованы
- Модульность и расширяемость
Состав архитектуры
Основные слои:
1. Presentation Layer (Слой представления)
- Ответственность: Пользовательский интерфейс и взаимодействие
- Компоненты: Controllers, Views, API endpoints, UI components
- Технологии: REST controllers, GraphQL resolvers, Web UI
- Взаимодействие: Получает запросы от пользователей, делегирует обработку Application Layer
2. Application Layer (Слой приложения)
- Ответственность: Координация бизнес-операций и оркестрация
- Компоненты: Application Services, Use Cases, Command/Query Handlers
- Технологии: Service classes, Transaction management
- Взаимодействие: Координирует работу Domain Layer, управляет транзакциями
3. Domain Layer (Доменный слой)
- Ответственность: Бизнес-логика и доменные правила
- Компоненты: Entities, Value Objects, Domain Services, Business Rules
- Технологии: Domain models, Business logic
- Взаимодействие: Содержит основную бизнес-логику, не зависит от внешних слоев
4. Infrastructure Layer (Инфраструктурный слой)
- Ответственность: Внешние зависимости и техническая реализация
- Компоненты: Repositories, External APIs, Database, File System
- Технологии: ORM, HTTP clients, Database drivers
- Взаимодействие: Реализует интерфейсы, определенные в Domain Layer
Дополнительные слои:
Cross-Cutting Concerns (Сквозные аспекты)
- Логирование — запись событий и ошибок
- Безопасность — аутентификация и авторизация
- Кэширование — хранение часто используемых данных
- Мониторинг — отслеживание производительности
Integration Layer (Слой интеграции)
- Внешние API — интеграция с внешними сервисами
- Message Queues — асинхронная обработка сообщений
- Event Handlers — обработка внешних событий
- Data Transformation — преобразование данных между системами
Слои
- Presentation Layer — пользовательский интерфейс
- Application Layer — бизнес-логика приложения
- Domain Layer — доменная логика
- Infrastructure Layer — внешние зависимости
Примеры реализации
🔧 Java (Spring Boot)
// Presentation Layer
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderApplicationService orderService;
@PostMapping
public ResponseEntity<OrderResponse> createOrder(@RequestBody CreateOrderRequest request) {
OrderResponse response = orderService.createOrder(request);
return ResponseEntity.ok(response);
}
}
// Application Layer
@Service
public class OrderApplicationService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private PaymentService paymentService;
public OrderResponse createOrder(CreateOrderRequest request) {
// Применение бизнес-правил
Order order = new Order(request);
// Обработка платежа
PaymentResult payment = paymentService.processPayment(order);
if (payment.isSuccess()) {
order.confirm();
orderRepository.save(order);
}
return OrderResponse.fromOrder(order);
}
}
// Domain Layer
public class Order {
private String id;
private String customerId;
private OrderStatus status;
private BigDecimal amount;
public void confirm() {
if (this.status != OrderStatus.DRAFT) {
throw new IllegalStateException("Cannot confirm non-draft order");
}
this.status = OrderStatus.CONFIRMED;
}
}
// Infrastructure Layer
@Repository
public class JpaOrderRepository implements OrderRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public void save(Order order) {
entityManager.persist(order);
}
}
🦀 Rust (Actix Web + Layered Structure)
use actix_web::{web, App, HttpServer, Result};
use serde::{Deserialize, Serialize};
// Presentation Layer
#[derive(Deserialize)]
pub struct CreateOrderRequest {
pub customer_id: String,
pub amount: f64,
}
#[derive(Serialize)]
pub struct OrderResponse {
pub id: String,
pub status: String,
pub amount: f64,
}
async fn create_order(
request: web::Json<CreateOrderRequest>,
order_service: web::Data<OrderApplicationService>,
) -> Result<web::Json<OrderResponse>> {
let response = order_service.create_order(request.into_inner()).await?;
Ok(web::Json(response))
}
// Application Layer
pub struct OrderApplicationService {
order_repository: Arc<dyn OrderRepository>,
payment_service: Arc<dyn PaymentService>,
}
impl OrderApplicationService {
pub async fn create_order(&self, request: CreateOrderRequest) -> Result<OrderResponse, String> {
// Применение бизнес-правил
let mut order = Order::new(request);
// Обработка платежа
let payment = self.payment_service.process_payment(&order).await?;
if payment.is_success {
order.confirm();
self.order_repository.save(&order).await?;
}
Ok(OrderResponse::from(order))
}
}
// Domain Layer
#[derive(Debug, Clone)]
pub struct Order {
pub id: String,
pub customer_id: String,
pub status: String,
pub amount: f64,
}
impl Order {
pub fn new(request: CreateOrderRequest) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
customer_id: request.customer_id,
status: "DRAFT".to_string(),
amount: request.amount,
}
}
pub fn confirm(&mut self) {
if self.status != "DRAFT" {
panic!("Cannot confirm non-draft order");
}
self.status = "CONFIRMED".to_string();
}
}
// Infrastructure Layer
pub struct PostgresOrderRepository {
pool: sqlx::PgPool,
}
#[async_trait]
impl OrderRepository for PostgresOrderRepository {
async fn save(&self, order: &Order) -> Result<(), String> {
sqlx::query!(
"INSERT INTO orders (id, customer_id, status, amount) VALUES ($1, $2, $3, $4)",
order.id, order.customer_id, order.status, order.amount
)
.execute(&self.pool)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
}
🟨 JavaScript (Node.js + Express)
const express = require('express');
const app = express();
// Presentation Layer
class OrderController {
constructor(orderService) {
this.orderService = orderService;
}
async createOrder(req, res) {
try {
const response = await this.orderService.createOrder(req.body);
res.json(response);
} catch (error) {
res.status(500).json({ error: error.message });
}
}
}
// Application Layer
class OrderApplicationService {
constructor(orderRepository, paymentService) {
this.orderRepository = orderRepository;
this.paymentService = paymentService;
}
async createOrder(request) {
// Применение бизнес-правил
const order = new Order(request);
// Обработка платежа
const payment = await this.paymentService.processPayment(order);
if (payment.isSuccess) {
order.confirm();
await this.orderRepository.save(order);
}
return new OrderResponse(order);
}
}
// Domain Layer
class Order {
constructor(data) {
this.id = data.id || Date.now().toString();
this.customerId = data.customerId;
this.status = 'DRAFT';
this.amount = data.amount;
}
confirm() {
if (this.status !== 'DRAFT') {
throw new Error('Cannot confirm non-draft order');
}
this.status = 'CONFIRMED';
}
}
class OrderResponse {
constructor(order) {
this.id = order.id;
this.status = order.status;
this.amount = order.amount;
}
}
// Infrastructure Layer
class MongoOrderRepository {
constructor(mongoClient) {
this.mongoClient = mongoClient;
}
async save(order) {
const db = this.mongoClient.db('ecommerce');
await db.collection('orders').replaceOne(
{ id: order.id },
order,
{ upsert: true }
);
}
}
// Настройка маршрутов
app.post('/api/orders', async (req, res) => {
const orderRepository = new MongoOrderRepository(mongoClient);
const paymentService = new StripePaymentService(stripeClient);
const orderService = new OrderApplicationService(orderRepository, paymentService);
const orderController = new OrderController(orderService);
await orderController.createOrder(req, res);
});
🐍 Python (FastAPI + Layered Structure)
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
import uuid
app = FastAPI()
# Presentation Layer
class CreateOrderRequest(BaseModel):
customer_id: str
amount: float
class OrderResponse(BaseModel):
id: str
status: str
amount: float
@app.post("/api/orders")
async def create_order(
request: CreateOrderRequest,
order_service: OrderApplicationService = Depends(get_order_service)
):
try:
response = await order_service.create_order(request)
return response
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# Application Layer
class OrderApplicationService:
def __init__(self, order_repository: OrderRepository, payment_service: PaymentService):
self.order_repository = order_repository
self.payment_service = payment_service
async def create_order(self, request: CreateOrderRequest) -> OrderResponse:
# Применение бизнес-правил
order = Order(request)
# Обработка платежа
payment = await self.payment_service.process_payment(order)
if payment.is_success:
order.confirm()
await self.order_repository.save(order)
return OrderResponse.from_order(order)
# Domain Layer
class Order:
def __init__(self, data):
self.id = str(uuid.uuid4())
self.customer_id = data.customer_id
self.status = "DRAFT"
self.amount = data.amount
def confirm(self):
if self.status != "DRAFT":
raise ValueError("Cannot confirm non-draft order")
self.status = "CONFIRMED"
# Infrastructure Layer
class PostgresOrderRepository:
def __init__(self, db_pool):
self.db_pool = db_pool
async def save(self, order: Order) -> None:
async with self.db_pool.acquire() as conn:
await conn.execute(
"INSERT INTO orders (id, customer_id, status, amount) VALUES ($1, $2, $3, $4) "
"ON CONFLICT (id) DO UPDATE SET customer_id = $2, status = $3, amount = $4",
order.id, order.customer_id, order.status, order.amount
)
# Dependency Injection
def get_order_service() -> OrderApplicationService:
order_repository = PostgresOrderRepository(db_pool)
payment_service = StripePaymentService(stripe_client)
return OrderApplicationService(order_repository, payment_service)
Serverless Architecture
Определение
Serverless Architecture — это архитектурный стиль, при котором приложение состоит из функций, которые выполняются в ответ на события. Разработчики не управляют серверами напрямую, а полагаются на облачные провайдеры для управления инфраструктурой, масштабированием и развертыванием.
Принципы работы
1. Functions as a Service (FaaS)
- Код выполняется в виде отдельных функций
- Функции запускаются по требованию
- Автоматическое управление жизненным циклом
- Изоляция выполнения функций
2. Event-driven execution
- Функции запускаются в ответ на события
- Различные триггеры: HTTP-запросы, сообщения, файлы, расписания
- Асинхронная обработка событий
- Высокая отзывчивость системы
3. Stateless functions
- Функции не сохраняют состояние между вызовами
- Все данные передаются через параметры
- Внешние хранилища для персистентности
- Горизонтальное масштабирование
4. Pay-per-execution
- Оплата только за фактическое выполнение
- Нет платы за простаивающие ресурсы
- Экономическая эффективность для переменных нагрузок
- Автоматическое масштабирование до нуля
Состав архитектуры
Основные компоненты:
1. Function Runtime (Среда выполнения функций)
- AWS Lambda — Amazon Web Services
- Azure Functions — Microsoft Azure
- Google Cloud Functions — Google Cloud Platform
- Cloudflare Workers — Cloudflare
- Vercel Functions — Vercel
2. Event Sources (Источники событий)
- HTTP Triggers — REST API, Webhooks
- Message Queues — SQS, Service Bus, Pub/Sub
- File Storage — S3, Blob Storage, Cloud Storage
- Database Events — DynamoDB Streams, Cosmos DB
- Scheduled Events — CloudWatch Events, Timer Triggers
3. Backend Services (Backend-сервисы)
- Database as a Service — DynamoDB, Cosmos DB, Firestore
- Storage Services — S3, Blob Storage, Cloud Storage
- Message Services — SNS, Event Grid, Pub/Sub
- Authentication — Cognito, Azure AD, Firebase Auth
- API Gateway — AWS API Gateway, Azure API Management
Паттерны архитектуры:
1. Event-Driven Processing
- Event Sourcing — хранение состояния как событий
- CQRS — разделение команд и запросов
- Saga Pattern — управление распределенными транзакциями
- Event Streaming — обработка потоков событий
2. Microservices Patterns
- Function per Service — одна функция на сервис
- Function per Use Case — одна функция на use case
- Function per Domain — группировка по доменам
- Function Composition — композиция функций
3. Integration Patterns
- API Gateway — единая точка входа
- Service Mesh — управление сервисами
- Event Bus — шина событий
- Message Routing — маршрутизация сообщений
Технологические решения:
Cloud Providers:
- AWS — Lambda, API Gateway, DynamoDB, S3
- Azure — Functions, Logic Apps, Cosmos DB, Blob Storage
- Google Cloud — Cloud Functions, Cloud Run, Firestore
- Multi-cloud — Serverless Framework, Terraform
Development Tools:
- Frameworks — Serverless Framework, AWS SAM, Azure Functions Core Tools
- Testing — LocalStack, SAM Local, Azure Functions Core Tools
- Monitoring — CloudWatch, Application Insights, Stackdriver
- CI/CD — GitHub Actions, Azure DevOps, AWS CodePipeline
Характеристики
- FaaS — Functions as a Service
- Event-driven — функции запускаются по событиям
- Auto-scaling — автоматическое масштабирование
- Pay-per-use — оплата только за использование
Примеры реализации
🔧 Java (AWS Lambda + Azure Functions)
// AWS Lambda функция
public class OrderProcessor implements RequestHandler<OrderEvent, String> {
@Override
public String handleRequest(OrderEvent event, Context context) {
// Обработка заказа
Order order = processOrder(event);
// Отправка уведомления
sendNotification(order);
return "Order processed successfully";
}
private Order processOrder(OrderEvent event) {
// Логика обработки заказа
return new Order(event);
}
private void sendNotification(Order order) {
// Отправка уведомления
}
}
// Azure Functions
@FunctionName("ProcessOrder")
public String processOrder(
@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS)
HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
String orderData = request.getBody().orElse("");
// Обработка заказа
Order order = processOrderData(orderData);
return "Order processed: " + order.getId();
}
🦀 Rust (AWS Lambda + Cloudflare Workers)
use lambda_runtime::{service_fn, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
// AWS Lambda функция
#[derive(Deserialize)]
struct OrderEvent {
order_id: String,
customer_id: String,
amount: f64,
}
#[derive(Serialize)]
struct OrderResponse {
message: String,
order_id: String,
}
async fn order_processor(event: LambdaEvent<OrderEvent>) -> Result<OrderResponse, Error> {
let order = event.payload;
// Обработка заказа
let processed_order = process_order(&order).await?;
// Отправка уведомления
send_notification(&processed_order).await?;
Ok(OrderResponse {
message: "Order processed successfully".to_string(),
order_id: order.order_id,
})
}
async fn process_order(event: &OrderEvent) -> Result<Order, Error> {
// Логика обработки заказа
Ok(Order {
id: event.order_id.clone(),
customer_id: event.customer_id.clone(),
amount: event.amount,
status: "PROCESSED".to_string(),
})
}
async fn send_notification(order: &Order) -> Result<(), Error> {
// Отправка уведомления через SNS или другой сервис
println!("Notification sent for order: {}", order.id);
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Error> {
lambda_runtime::run(service_fn(order_processor)).await
}
// Cloudflare Workers
use worker::*;
#[event(fetch)]
async fn main(req: Request, env: Env, _ctx: Context) -> Result<Response> {
let router = Router::new();
router
.post_async("/process-order", |mut req, _ctx| async move {
let order_data: OrderEvent = req.json().await?;
// Обработка заказа
let order = process_order_worker(&order_data).await?;
// Отправка уведомления
send_notification_worker(&order).await?;
Response::ok("Order processed successfully")
})
.run(req, env)
.await
}
🟨 JavaScript (AWS Lambda + Vercel Functions)
// AWS Lambda функция
const AWS = require('aws-sdk');
const sns = new AWS.SNS();
exports.handler = async (event) => {
try {
// Обработка заказа
const order = await processOrder(event);
// Отправка уведомления
await sendNotification(order);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Order processed successfully',
orderId: order.id
})
};
} catch (error) {
return {
statusCode: 500,
body: JSON.stringify({ error: error.message })
};
}
};
async function processOrder(event) {
// Логика обработки заказа
return {
id: event.orderId,
customerId: event.customerId,
amount: event.amount,
status: 'PROCESSED'
};
}
async function sendNotification(order) {
// Отправка уведомления через SNS
const params = {
Message: `Order ${order.id} has been processed`,
TopicArn: process.env.SNS_TOPIC_ARN
};
await sns.publish(params).promise();
}
// Vercel Functions
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const orderData = req.body;
// Обработка заказа
const order = await processOrderVercel(orderData);
// Отправка уведомления
await sendNotificationVercel(order);
res.status(200).json({
message: 'Order processed successfully',
orderId: order.id
});
} catch (error) {
res.status(500).json({ error: error.message });
}
}
async function processOrderVercel(orderData) {
// Логика обработки заказа
return {
id: orderData.orderId,
customerId: orderData.customerId,
amount: orderData.amount,
status: 'PROCESSED'
};
}
async function sendNotificationVercel(order) {
// Отправка уведомления через внешний API
await fetch(process.env.NOTIFICATION_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: `Order ${order.id} has been processed`,
orderId: order.id
})
});
}
🐍 Python (AWS Lambda + Google Cloud Functions)
import json
import boto3
from typing import Dict, Any
# AWS Lambda функция
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
try:
# Обработка заказа
order = process_order(event)
# Отправка уведомления
send_notification(order)
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Order processed successfully',
'orderId': order['id']
})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}
def process_order(event: Dict[str, Any]) -> Dict[str, Any]:
# Логика обработки заказа
return {
'id': event['orderId'],
'customerId': event['customerId'],
'amount': event['amount'],
'status': 'PROCESSED'
}
def send_notification(order: Dict[str, Any]) -> None:
# Отправка уведомления через SNS
sns = boto3.client('sns')
sns.publish(
TopicArn=os.environ['SNS_TOPIC_ARN'],
Message=f"Order {order['id']} has been processed"
)
# Google Cloud Functions
from google.cloud import pubsub_v1
import functions_framework
@functions_framework.http
def process_order_gcp(request):
if request.method != 'POST':
return 'Method not allowed', 405
try:
order_data = request.get_json()
# Обработка заказа
order = process_order_gcp_logic(order_data)
# Отправка уведомления
send_notification_gcp(order)
return {
'message': 'Order processed successfully',
'orderId': order['id']
}
except Exception as e:
return {'error': str(e)}, 500
def process_order_gcp_logic(order_data: Dict[str, Any]) -> Dict[str, Any]:
# Логика обработки заказа
return {
'id': order_data['orderId'],
'customerId': order_data['customerId'],
'amount': order_data['amount'],
'status': 'PROCESSED'
}
def send_notification_gcp(order: Dict[str, Any]) -> None:
# Отправка уведомления через Pub/Sub
publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(
os.environ['GCP_PROJECT_ID'],
os.environ['PUBSUB_TOPIC_NAME']
)
message_data = f"Order {order['id']} has been processed".encode('utf-8')
publisher.publish(topic_path, message_data)
Выбор архитектурного стиля
Факторы выбора
- Размер команды — маленькая команда → монолит
- Сложность домена — сложный домен → микросервисы
- Требования к масштабированию — высокие → микросервисы
- Технологическое разнообразие — разные технологии → микросервисы
- Время разработки — быстрое время → монолит
Рекомендации
- Начните с монолита — для большинства проектов
- Переходите к микросервисам — когда монолит становится проблемой
- Используйте EDA — для асинхронной обработки
- Рассмотрите CQRS — для сложных read/write требований
- Применяйте Hexagonal — для изоляции бизнес-логики