- Добавлены collapsible sections для монолитной архитектуры (Java, Rust, JS, Python) - Добавлены collapsible sections для микросервисной архитектуры (Java, Rust, JS, Python) - Добавлены collapsible sections для Event-Driven Architecture (Java, Rust, JS, Python) - Добавлены collapsible sections для CQRS (Java, Rust, JS, Python) - Добавлены collapsible sections для Hexagonal Architecture (Java, Rust, JS, Python) - Добавлены collapsible sections для Layered Architecture (Java, Rust, JS, Python) - Добавлены collapsible sections для Serverless Architecture (Java, Rust, JS, Python) - Использованы эмодзи иконки для каждого языка программирования - Обеспечена полная совместимость с любыми Markdown редакторами - Примеры кода организованы в удобные скрывающиеся блоки
2254 lines
64 KiB
Markdown
2254 lines
64 KiB
Markdown
# Архитектурные стили и паттерны
|
|
|
|
## Навигация
|
|
- [🏠 Главная страница курса](../../../README.md)
|
|
- [📚 Модуль 1](../README.md)
|
|
|
|
---
|
|
|
|
## Монолитная архитектура
|
|
|
|
### Определение
|
|
**Монолитная архитектура** — это архитектурный стиль, при котором все компоненты приложения объединены в единое развертываемое приложение.
|
|
|
|
### Характеристики
|
|
- **Единое развертывание** — все компоненты развертываются вместе
|
|
- **Общая база данных** — все модули используют одну БД
|
|
- **Единый технологический стек** — один язык, один фреймворк
|
|
- **Прямые вызовы** — компоненты вызывают друг друга напрямую
|
|
|
|
### Примеры реализации
|
|
|
|
<details>
|
|
<summary><strong>🔧 Java (Spring Boot)</strong></summary>
|
|
|
|
```java
|
|
// Монолитное приложение 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);
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🦀 Rust (Actix Web)</strong></summary>
|
|
|
|
```rust
|
|
// Монолитное приложение 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
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🟨 JavaScript (Node.js)</strong></summary>
|
|
|
|
```javascript
|
|
// Монолитное приложение 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');
|
|
});
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🐍 Python (FastAPI)</strong></summary>
|
|
|
|
```python
|
|
# Монолитное приложение 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))
|
|
```
|
|
|
|
</details>
|
|
|
|
### Преимущества
|
|
- **Простота разработки** — легко начать проект
|
|
- **Простота тестирования** — все в одном процессе
|
|
- **Простота развертывания** — один артефакт
|
|
- **Производительность** — нет сетевых вызовов
|
|
|
|
### Недостатки
|
|
- **Сложность масштабирования** — нужно масштабировать все приложение
|
|
- **Технологические ограничения** — привязанность к одному стеку
|
|
- **Риск сбоев** — падение одного компонента влияет на все
|
|
- **Сложность команд** — команды работают над одним кодом
|
|
|
|
## Микросервисная архитектура
|
|
|
|
### Определение
|
|
**Микросервисная архитектура** — это архитектурный стиль, при котором приложение разбивается на множество небольших, независимых сервисов.
|
|
|
|
### Характеристики
|
|
- **Независимые сервисы** — каждый сервис работает автономно
|
|
- **Отдельные базы данных** — каждый сервис имеет свою БД
|
|
- **Разные технологии** — сервисы могут использовать разные стеки
|
|
- **Сетевое взаимодействие** — сервисы общаются по сети
|
|
|
|
### Примеры реализации
|
|
|
|
<details>
|
|
<summary><strong>🔧 Java (Spring Boot)</strong></summary>
|
|
|
|
```java
|
|
// 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);
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🦀 Rust (Actix Web)</strong></summary>
|
|
|
|
```rust
|
|
// 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))
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🟨 JavaScript (Node.js)</strong></summary>
|
|
|
|
```javascript
|
|
// 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 });
|
|
}
|
|
});
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🐍 Python (FastAPI)</strong></summary>
|
|
|
|
```python
|
|
# 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))
|
|
```
|
|
|
|
</details>
|
|
|
|
### Преимущества
|
|
- **Независимое масштабирование** — каждый сервис масштабируется отдельно
|
|
- **Технологическое разнообразие** — разные технологии для разных задач
|
|
- **Независимые команды** — команды работают над разными сервисами
|
|
- **Отказоустойчивость** — падение одного сервиса не влияет на другие
|
|
|
|
### Недостатки
|
|
- **Сложность разработки** — нужно управлять множеством сервисов
|
|
- **Сетевая задержка** — вызовы между сервисами медленнее
|
|
- **Сложность тестирования** — нужно тестировать интеграции
|
|
- **Сложность развертывания** — нужно координировать развертывание
|
|
|
|
## Event-Driven Architecture (EDA)
|
|
|
|
### Определение
|
|
**Event-Driven Architecture** — это архитектурный стиль, основанный на производстве, обнаружении, потреблении и реакции на события.
|
|
|
|
### Характеристики
|
|
- **События** — значимые изменения в системе
|
|
- **Асинхронность** — события обрабатываются асинхронно
|
|
- **Слабая связанность** — компоненты не знают друг о друге
|
|
- **Масштабируемость** — легко добавлять новых потребителей
|
|
|
|
### Примеры реализации
|
|
|
|
<details>
|
|
<summary><strong>🔧 Java (Spring Boot)</strong></summary>
|
|
|
|
```java
|
|
// Доменное событие
|
|
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());
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🦀 Rust (Actix Web + Tokio)</strong></summary>
|
|
|
|
```rust
|
|
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;
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🟨 JavaScript (Node.js + EventEmitter)</strong></summary>
|
|
|
|
```javascript
|
|
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);
|
|
});
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🐍 Python (FastAPI + asyncio)</strong></summary>
|
|
|
|
```python
|
|
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)
|
|
```
|
|
|
|
</details>
|
|
|
|
## CQRS (Command Query Responsibility Segregation)
|
|
|
|
### Определение
|
|
**CQRS** — это паттерн, который разделяет операции чтения и записи данных на разные модели.
|
|
|
|
### Характеристики
|
|
- **Разделение команд и запросов** — разные модели для записи и чтения
|
|
- **Оптимизация** — модели оптимизированы для своих задач
|
|
- **Масштабируемость** — можно масштабировать чтение и запись отдельно
|
|
- **Сложность** — увеличивает сложность системы
|
|
|
|
### Примеры реализации
|
|
|
|
<details>
|
|
<summary><strong>🔧 Java (Spring Boot)</strong></summary>
|
|
|
|
```java
|
|
// 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());
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🦀 Rust (Actix Web + Diesel)</strong></summary>
|
|
|
|
```rust
|
|
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
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🟨 JavaScript (Node.js + MongoDB)</strong></summary>
|
|
|
|
```javascript
|
|
// 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);
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🐍 Python (FastAPI + SQLAlchemy)</strong></summary>
|
|
|
|
```python
|
|
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)
|
|
```
|
|
|
|
</details>
|
|
|
|
## Hexagonal Architecture (Ports and Adapters)
|
|
|
|
### Определение
|
|
**Hexagonal Architecture** — это архитектурный паттерн, который изолирует бизнес-логику от внешних зависимостей.
|
|
|
|
### Характеристики
|
|
- **Порты** — интерфейсы для взаимодействия с внешним миром
|
|
- **Адаптеры** — реализации портов для конкретных технологий
|
|
- **Изоляция** — бизнес-логика не зависит от внешних технологий
|
|
- **Тестируемость** — легко тестировать с mock-объектами
|
|
|
|
### Примеры реализации
|
|
|
|
<details>
|
|
<summary><strong>🔧 Java (Spring Boot)</strong></summary>
|
|
|
|
```java
|
|
// 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 реализация
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🦀 Rust (Actix Web + Dependency Injection)</strong></summary>
|
|
|
|
```rust
|
|
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,
|
|
}))
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🟨 JavaScript (Node.js + Dependency Injection)</strong></summary>
|
|
|
|
```javascript
|
|
// 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
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🐍 Python (FastAPI + Dependency Injection)</strong></summary>
|
|
|
|
```python
|
|
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
|
|
)
|
|
```
|
|
|
|
</details>
|
|
|
|
## Layered Architecture
|
|
|
|
### Определение
|
|
**Layered Architecture** — это архитектурный паттерн, который организует код в горизонтальные слои.
|
|
|
|
### Слои
|
|
1. **Presentation Layer** — пользовательский интерфейс
|
|
2. **Application Layer** — бизнес-логика приложения
|
|
3. **Domain Layer** — доменная логика
|
|
4. **Infrastructure Layer** — внешние зависимости
|
|
|
|
### Примеры реализации
|
|
|
|
<details>
|
|
<summary><strong>🔧 Java (Spring Boot)</strong></summary>
|
|
|
|
```java
|
|
// 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);
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🦀 Rust (Actix Web + Layered Structure)</strong></summary>
|
|
|
|
```rust
|
|
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(())
|
|
}
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🟨 JavaScript (Node.js + Express)</strong></summary>
|
|
|
|
```javascript
|
|
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);
|
|
});
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🐍 Python (FastAPI + Layered Structure)</strong></summary>
|
|
|
|
```python
|
|
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)
|
|
```
|
|
|
|
</details>
|
|
|
|
## Serverless Architecture
|
|
|
|
### Определение
|
|
**Serverless Architecture** — это архитектурный стиль, при котором приложение состоит из функций, которые выполняются в ответ на события.
|
|
|
|
### Характеристики
|
|
- **FaaS** — Functions as a Service
|
|
- **Event-driven** — функции запускаются по событиям
|
|
- **Auto-scaling** — автоматическое масштабирование
|
|
- **Pay-per-use** — оплата только за использование
|
|
|
|
### Примеры реализации
|
|
|
|
<details>
|
|
<summary><strong>🔧 Java (AWS Lambda + Azure Functions)</strong></summary>
|
|
|
|
```java
|
|
// 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();
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🦀 Rust (AWS Lambda + Cloudflare Workers)</strong></summary>
|
|
|
|
```rust
|
|
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
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🟨 JavaScript (AWS Lambda + Vercel Functions)</strong></summary>
|
|
|
|
```javascript
|
|
// 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
|
|
})
|
|
});
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>🐍 Python (AWS Lambda + Google Cloud Functions)</strong></summary>
|
|
|
|
```python
|
|
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)
|
|
```
|
|
|
|
</details>
|
|
|
|
## Выбор архитектурного стиля
|
|
|
|
### Факторы выбора
|
|
1. **Размер команды** — маленькая команда → монолит
|
|
2. **Сложность домена** — сложный домен → микросервисы
|
|
3. **Требования к масштабированию** — высокие → микросервисы
|
|
4. **Технологическое разнообразие** — разные технологии → микросервисы
|
|
5. **Время разработки** — быстрое время → монолит
|
|
|
|
### Рекомендации
|
|
- **Начните с монолита** — для большинства проектов
|
|
- **Переходите к микросервисам** — когда монолит становится проблемой
|
|
- **Используйте EDA** — для асинхронной обработки
|
|
- **Рассмотрите CQRS** — для сложных read/write требований
|
|
- **Применяйте Hexagonal** — для изоляции бизнес-логики
|