diff --git a/modules/module1/sections/05-architectural-styles-patterns.md b/modules/module1/sections/05-architectural-styles-patterns.md index 4deeab4..9ccb052 100644 --- a/modules/module1/sections/05-architectural-styles-patterns.md +++ b/modules/module1/sections/05-architectural-styles-patterns.md @@ -17,7 +17,11 @@ - **Единый технологический стек** — один язык, один фреймворк - **Прямые вызовы** — компоненты вызывают друг друга напрямую -### Пример реализации +### Примеры реализации + +
+🔧 Java (Spring Boot) + ```java // Монолитное приложение e-commerce @SpringBootApplication @@ -52,6 +56,148 @@ public class EcommerceController { } ``` +
+ +
+🦀 Rust (Actix Web) + +```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, +} + +#[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, + user_service: web::Data, + order_service: web::Data, + payment_service: web::Data, +) -> Result> { + // Вся логика в одном месте + 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) + +```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'); +}); +``` + +
+ +
+🐍 Python (FastAPI) + +```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)) +``` + +
+ ### Преимущества - **Простота разработки** — легко начать проект - **Простота тестирования** — все в одном процессе @@ -75,7 +221,11 @@ public class EcommerceController { - **Разные технологии** — сервисы могут использовать разные стеки - **Сетевое взаимодействие** — сервисы общаются по сети -### Пример реализации +### Примеры реализации + +
+🔧 Java (Spring Boot) + ```java // User Service @RestController @@ -124,6 +274,158 @@ public class PaymentController { } ``` +
+ +
+🦀 Rust (Actix Web) + +```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) -> Result> { + 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, + user_client: web::Data, +) -> Result> { + // Вызов внешнего сервиса + 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, +) -> Result> { + let result = payment_service.process_payment(payment_data.into_inner()).await?; + Ok(web::Json(result)) +} +``` + +
+ +
+🟨 JavaScript (Node.js) + +```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 }); + } +}); +``` + +
+ +
+🐍 Python (FastAPI) + +```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)) +``` + +
+ ### Преимущества - **Независимое масштабирование** — каждый сервис масштабируется отдельно - **Технологическое разнообразие** — разные технологии для разных задач @@ -147,7 +449,11 @@ public class PaymentController { - **Слабая связанность** — компоненты не знают друг о друге - **Масштабируемость** — легко добавлять новых потребителей -### Пример реализации +### Примеры реализации + +
+🔧 Java (Spring Boot) + ```java // Доменное событие public class OrderCreatedEvent { @@ -209,6 +515,233 @@ public class OrderCreatedEventHandler { } ``` +
+ +
+🦀 Rust (Actix Web + Tokio) + +```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, +} + +// Event Publisher +pub struct EventPublisher { + sender: mpsc::UnboundedSender, +} + +impl EventPublisher { + pub fn new(sender: mpsc::UnboundedSender) -> 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 { + 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) + +```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); +}); +``` + +
+ +
+🐍 Python (FastAPI + asyncio) + +```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) +``` + +
+ ## CQRS (Command Query Responsibility Segregation) ### Определение @@ -220,7 +753,11 @@ public class OrderCreatedEventHandler { - **Масштабируемость** — можно масштабировать чтение и запись отдельно - **Сложность** — увеличивает сложность системы -### Пример реализации +### Примеры реализации + +
+🔧 Java (Spring Boot) + ```java // Command Model (Write) @Entity @@ -293,6 +830,215 @@ public class OrderQueryHandler { } ``` +
+ +
+🦀 Rust (Actix Web + Diesel) + +```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, +} + +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, + 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, String> { + self.order_summary_repository.find_by_customer_id(&query.customer_id).await + } +} +``` + +
+ +
+🟨 JavaScript (Node.js + MongoDB) + +```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); + } +} +``` + +
+ +
+🐍 Python (FastAPI + SQLAlchemy) + +```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) +``` + +
+ ## Hexagonal Architecture (Ports and Adapters) ### Определение @@ -304,7 +1050,11 @@ public class OrderQueryHandler { - **Изоляция** — бизнес-логика не зависит от внешних технологий - **Тестируемость** — легко тестировать с mock-объектами -### Пример реализации +### Примеры реализации + +
+🔧 Java (Spring Boot) + ```java // Domain Layer (внутренний круг) public class Order { @@ -399,6 +1149,381 @@ public class EmailNotificationService implements NotificationService { } ``` +
+ +
+🦀 Rust (Actix Web + Dependency Injection) + +```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, String>; +} + +#[async_trait] +pub trait PaymentService: Send + Sync { + async fn process_payment(&self, request: PaymentRequest) -> Result; +} + +#[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, + payment_service: Arc, + notification_service: Arc, +} + +impl OrderService { + pub fn new( + order_repository: Arc, + payment_service: Arc, + notification_service: Arc, + ) -> 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, 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) + +```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 + }); + } +} +``` + +
+ +
+🐍 Python (FastAPI + Dependency Injection) + +```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 + ) +``` + +
+ ## Layered Architecture ### Определение @@ -410,7 +1535,11 @@ public class EmailNotificationService implements NotificationService { 3. **Domain Layer** — доменная логика 4. **Infrastructure Layer** — внешние зависимости -### Пример реализации +### Примеры реализации + +
+🔧 Java (Spring Boot) + ```java // Presentation Layer @RestController @@ -482,6 +1611,294 @@ public class JpaOrderRepository implements OrderRepository { } ``` +
+ +
+🦀 Rust (Actix Web + Layered Structure) + +```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, + order_service: web::Data, +) -> Result> { + let response = order_service.create_order(request.into_inner()).await?; + Ok(web::Json(response)) +} + +// Application Layer +pub struct OrderApplicationService { + order_repository: Arc, + payment_service: Arc, +} + +impl OrderApplicationService { + pub async fn create_order(&self, request: CreateOrderRequest) -> Result { + // Применение бизнес-правил + 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) + +```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); +}); +``` + +
+ +
+🐍 Python (FastAPI + Layered Structure) + +```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) +``` + +
+ ## Serverless Architecture ### Определение @@ -493,7 +1910,11 @@ public class JpaOrderRepository implements OrderRepository { - **Auto-scaling** — автоматическое масштабирование - **Pay-per-use** — оплата только за использование -### Пример реализации +### Примеры реализации + +
+🔧 Java (AWS Lambda + Azure Functions) + ```java // AWS Lambda функция public class OrderProcessor implements RequestHandler { @@ -535,6 +1956,286 @@ public String processOrder( } ``` +
+ +
+🦀 Rust (AWS Lambda + Cloudflare Workers) + +```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) -> Result { + 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 { + // Логика обработки заказа + 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 { + 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) + +```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 + }) + }); +} +``` + +
+ +
+🐍 Python (AWS Lambda + Google Cloud Functions) + +```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) +``` + +
+ ## Выбор архитектурного стиля ### Факторы выбора