Files
Alexander Abakumov 15e744da49 Initial commit: Complete Software Architecture course with 11 modules
- Added comprehensive course structure with 11 modules
- Included theoretical content, practical examples, and tasks
- Multi-language code examples (Java, Rust, Node.js, Python)
- Detailed analysis of architectural approaches and trade-offs
- Complete caching strategies documentation
- Architecture examples from real-world systems (Twitter, Netflix, Uber)
- Structured navigation and learning path
- Ready for educational use
2025-09-09 22:08:07 +03:00

87 KiB
Raw Permalink Blame History

Модуль 7: Документирование архитектуры

Содержание модуля

  1. Основы архитектурной документации
  2. C4 Model
  3. PlantUML
  4. Architecture Decision Records (ADR)
  5. Техническая документация
  6. Коммуникация архитектуры

Основы архитектурной документации

Зачем нужна архитектурная документация

1. Коммуникация

  • Общий язык для команды
  • Понимание системы новыми участниками
  • Обсуждение архитектурных решений

2. Планирование

  • Основа для принятия решений
  • Анализ влияния изменений
  • Планирование миграций

3. Поддержка

  • Быстрое понимание системы
  • Отладка проблем
  • Обучение команды

Принципы архитектурной документации

1. Living Documentation

Принцип: Документация должна быть живой и актуальной.

Технический пример - Living Documentation с автоматизацией:

# Архитектура системы E-Commerce Platform

> **Последнее обновление:** 2024-01-15  
> **Версия:** 2.1  
> **Ответственный:** Архитектор команды  
> **Статус:** Активная разработка  
> **Следующий обзор:** 2024-02-15  

## Изменения в версии 2.1
- ✅ Добавлен новый микросервис Payment Service
- ✅ Обновлена схема базы данных (добавлены индексы для производительности)
- ✅ Изменена стратегия кэширования (Redis Cluster)
- 🔄 В процессе: Миграция на Kubernetes
- 📋 Планируется: Внедрение Event Sourcing

## Архитектурные решения
- [ADR-001: Выбор микросервисной архитектуры](./adr/001-microservices-architecture.md)
- [ADR-002: Использование PostgreSQL](./adr/002-postgresql-database.md)
- [ADR-003: Redis для кэширования](./adr/003-redis-caching.md)
- [ADR-004: Kubernetes для оркестрации](./adr/004-kubernetes-orchestration.md)

## Метрики системы
- **Uptime:** 99.9% (последние 30 дней)
- **Response Time:** P95 < 200ms
- **Throughput:** 10,000 RPS
- **Error Rate:** < 0.1%

Технический пример - Automated Documentation Generation:

// Architecture Documentation Generator
@Component
public class ArchitectureDocumentationGenerator {
    
    @Autowired
    private ServiceRegistry serviceRegistry;
    
    @Autowired
    private DatabaseSchemaAnalyzer schemaAnalyzer;
    
    @Autowired
    private ApiSpecificationGenerator apiGenerator;
    
    @Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00
    public void generateDocumentation() {
        log.info("Starting architecture documentation generation");
        
        try {
            // Генерируем обновленную документацию
            generateSystemOverview();
            generateServiceCatalog();
            generateDatabaseSchema();
            generateApiDocumentation();
            generateDeploymentDiagrams();
            
            // Обновляем метрики
            updateSystemMetrics();
            
            // Отправляем уведомления
            notifyTeamAboutUpdates();
            
            log.info("Architecture documentation generation completed");
            
        } catch (Exception e) {
            log.error("Failed to generate architecture documentation", e);
        }
    }
    
    private void generateSystemOverview() {
        SystemOverview overview = SystemOverview.builder()
            .systemName("E-Commerce Platform")
            .version(getCurrentVersion())
            .lastUpdated(LocalDateTime.now())
            .services(serviceRegistry.getAllServices())
            .databases(schemaAnalyzer.getDatabaseInfo())
            .build();
        
        String markdown = convertToMarkdown(overview);
        writeToFile("docs/system-overview.md", markdown);
    }
    
    private void generateServiceCatalog() {
        List<ServiceInfo> services = serviceRegistry.getAllServices();
        
        StringBuilder catalog = new StringBuilder();
        catalog.append("# Service Catalog\n\n");
        catalog.append("| Service | Version | Status | Health | Dependencies |\n");
        catalog.append("|---------|---------|--------|--------|-------------|\n");
        
        for (ServiceInfo service : services) {
            catalog.append(String.format("| %s | %s | %s | %s | %s |\n",
                service.getName(),
                service.getVersion(),
                service.getStatus(),
                service.getHealthStatus(),
                String.join(", ", service.getDependencies())
            ));
        }
        
        writeToFile("docs/service-catalog.md", catalog.toString());
    }
    
    private void generateDatabaseSchema() {
        DatabaseSchema schema = schemaAnalyzer.analyzeSchema();
        
        StringBuilder schemaDoc = new StringBuilder();
        schemaDoc.append("# Database Schema\n\n");
        
        for (TableInfo table : schema.getTables()) {
            schemaDoc.append(String.format("## %s\n\n", table.getName()));
            schemaDoc.append(String.format("**Description:** %s\n\n", table.getDescription()));
            schemaDoc.append("| Column | Type | Nullable | Key | Description |\n");
            schemaDoc.append("|--------|------|----------|-----|-------------|\n");
            
            for (ColumnInfo column : table.getColumns()) {
                schemaDoc.append(String.format("| %s | %s | %s | %s | %s |\n",
                    column.getName(),
                    column.getType(),
                    column.isNullable() ? "Yes" : "No",
                    column.getKeyType(),
                    column.getDescription()
                ));
            }
            schemaDoc.append("\n");
        }
        
        writeToFile("docs/database-schema.md", schemaDoc.toString());
    }
    
    private void generateApiDocumentation() {
        List<ApiSpecification> apiSpecs = apiGenerator.generateSpecifications();
        
        for (ApiSpecification spec : apiSpecs) {
            String openApiSpec = convertToOpenApi(spec);
            writeToFile(String.format("docs/api/%s-openapi.yaml", spec.getServiceName()), openApiSpec);
            
            String markdownDoc = convertToMarkdown(spec);
            writeToFile(String.format("docs/api/%s.md", spec.getServiceName()), markdownDoc);
        }
    }
    
    private void generateDeploymentDiagrams() {
        // Генерируем PlantUML диаграммы
        String deploymentDiagram = generatePlantUMLDeploymentDiagram();
        writeToFile("docs/diagrams/deployment.puml", deploymentDiagram);
        
        // Конвертируем в изображения
        convertPlantUMLToImages("docs/diagrams/deployment.puml");
    }
    
    private void updateSystemMetrics() {
        SystemMetrics metrics = collectSystemMetrics();
        
        StringBuilder metricsDoc = new StringBuilder();
        metricsDoc.append("# System Metrics\n\n");
        metricsDoc.append(String.format("**Last Updated:** %s\n\n", LocalDateTime.now()));
        metricsDoc.append("## Performance Metrics\n");
        metricsDoc.append(String.format("- **Uptime:** %.2f%%\n", metrics.getUptime()));
        metricsDoc.append(String.format("- **Response Time P95:** %dms\n", metrics.getResponseTimeP95()));
        metricsDoc.append(String.format("- **Throughput:** %d RPS\n", metrics.getThroughput()));
        metricsDoc.append(String.format("- **Error Rate:** %.2f%%\n", metrics.getErrorRate()));
        
        writeToFile("docs/system-metrics.md", metricsDoc.toString());
    }
    
    private void notifyTeamAboutUpdates() {
        // Отправляем уведомления в Slack/Teams
        notificationService.sendNotification(
            "Architecture documentation has been updated",
            "docs/system-overview.md"
        );
    }
}

// Service Registry для автоматического обнаружения сервисов
@Component
public class ServiceRegistry {
    
    @Autowired
    private DiscoveryClient discoveryClient;
    
    @Autowired
    private RestTemplate restTemplate;
    
    public List<ServiceInfo> getAllServices() {
        List<ServiceInfo> services = new ArrayList<>();
        
        List<String> serviceNames = discoveryClient.getServices();
        
        for (String serviceName : serviceNames) {
            List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
            
            if (!instances.isEmpty()) {
                ServiceInstance instance = instances.get(0);
                
                try {
                    ServiceInfo serviceInfo = getServiceInfo(instance);
                    services.add(serviceInfo);
                } catch (Exception e) {
                    log.warn("Failed to get info for service: {}", serviceName, e);
                }
            }
        }
        
        return services;
    }
    
    private ServiceInfo getServiceInfo(ServiceInstance instance) {
        String healthUrl = String.format("http://%s:%d/actuator/health", 
            instance.getHost(), instance.getPort());
        
        ResponseEntity<Map> healthResponse = restTemplate.getForEntity(healthUrl, Map.class);
        
        String infoUrl = String.format("http://%s:%d/actuator/info", 
            instance.getHost(), instance.getPort());
        
        ResponseEntity<Map> infoResponse = restTemplate.getForEntity(infoUrl, Map.class);
        
        return ServiceInfo.builder()
            .name(instance.getServiceId())
            .version(getVersionFromInfo(infoResponse.getBody()))
            .status(healthResponse.getStatusCode().is2xxSuccessful() ? "UP" : "DOWN")
            .healthStatus(healthResponse.getBody().get("status").toString())
            .dependencies(getDependenciesFromInfo(infoResponse.getBody()))
            .build();
    }
    
    private String getVersionFromInfo(Map<String, Object> info) {
        Map<String, Object> build = (Map<String, Object>) info.get("build");
        return build != null ? build.get("version").toString() : "unknown";
    }
    
    private List<String> getDependenciesFromInfo(Map<String, Object> info) {
        // Логика извлечения зависимостей из actuator/info
        return new ArrayList<>();
    }
}

// Database Schema Analyzer
@Component
public class DatabaseSchemaAnalyzer {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public DatabaseSchema analyzeSchema() {
        List<TableInfo> tables = new ArrayList<>();
        
        // Получаем список таблиц
        List<String> tableNames = getTableNames();
        
        for (String tableName : tableNames) {
            TableInfo table = analyzeTable(tableName);
            tables.add(table);
        }
        
        return new DatabaseSchema(tables);
    }
    
    private List<String> getTableNames() {
        String sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'";
        return jdbcTemplate.queryForList(sql, String.class);
    }
    
    private TableInfo analyzeTable(String tableName) {
        // Получаем информацию о колонках
        String columnSql = """
            SELECT 
                column_name,
                data_type,
                is_nullable,
                column_key,
                column_comment
            FROM information_schema.columns 
            WHERE table_name = ? AND table_schema = 'public'
            """;
        
        List<ColumnInfo> columns = jdbcTemplate.query(columnSql, 
            new Object[]{tableName}, 
            (rs, rowNum) -> new ColumnInfo(
                rs.getString("column_name"),
                rs.getString("data_type"),
                "YES".equals(rs.getString("is_nullable")),
                rs.getString("column_key"),
                rs.getString("column_comment")
            )
        );
        
        // Получаем информацию о таблице
        String tableSql = """
            SELECT 
                table_comment,
                table_rows,
                data_length
            FROM information_schema.tables 
            WHERE table_name = ? AND table_schema = 'public'
            """;
        
        Map<String, Object> tableInfo = jdbcTemplate.queryForMap(tableSql, tableName);
        
        return new TableInfo(
            tableName,
            (String) tableInfo.get("table_comment"),
            columns
        );
    }
}

// API Specification Generator
@Component
public class ApiSpecificationGenerator {
    
    @Autowired
    private RestTemplate restTemplate;
    
    public List<ApiSpecification> generateSpecifications() {
        List<ApiSpecification> specifications = new ArrayList<>();
        
        List<ServiceInfo> services = serviceRegistry.getAllServices();
        
        for (ServiceInfo service : services) {
            try {
                ApiSpecification spec = generateSpecificationForService(service);
                specifications.add(spec);
            } catch (Exception e) {
                log.warn("Failed to generate API spec for service: {}", service.getName(), e);
            }
        }
        
        return specifications;
    }
    
    private ApiSpecification generateSpecificationForService(ServiceInfo service) {
        // Получаем OpenAPI спецификацию от сервиса
        String openApiUrl = String.format("http://%s:%d/v3/api-docs", 
            service.getHost(), service.getPort());
        
        ResponseEntity<Map> response = restTemplate.getForEntity(openApiUrl, Map.class);
        Map<String, Object> openApiSpec = response.getBody();
        
        return ApiSpecification.builder()
            .serviceName(service.getName())
            .version(service.getVersion())
            .openApiSpec(openApiSpec)
            .endpoints(extractEndpoints(openApiSpec))
            .build();
    }
    
    private List<EndpointInfo> extractEndpoints(Map<String, Object> openApiSpec) {
        // Логика извлечения endpoints из OpenAPI спецификации
        return new ArrayList<>();
    }
}

2. Just Enough Documentation

Принцип: Документируйте достаточно, но не слишком много.

3. Audience-Driven

Принцип: Документация должна быть ориентирована на аудиторию.

Типы архитектурной документации

1. High-Level Architecture

  • Общий обзор системы
  • Основные компоненты
  • Ключевые взаимодействия

2. Detailed Design

  • Детальное описание компонентов
  • API спецификации
  • Схемы баз данных

3. Operational Documentation

  • Процедуры развертывания
  • Мониторинг и алертинг
  • Troubleshooting guides

C4 Model

Что такое C4 Model

C4 Model — методология для документирования архитектуры программного обеспечения через четыре уровня абстракции.

Уровни C4 Model

1. Context (Контекст)

Цель: Показать систему в контексте её окружения.

@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml

title System Context diagram for Internet Banking System

Person(customer, "Customer", "A customer of the bank, with personal bank accounts.")
Person(customerService, "Customer Service", "Customer service representative of the bank.")

System(bankingSystem, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")

System_Ext(emailSystem, "E-mail System", "The internal Microsoft Exchange e-mail system.")
System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")

Rel(customer, bankingSystem, "Uses", "HTTPS")
Rel(customerService, bankingSystem, "Uses", "HTTPS")
Rel(bankingSystem, emailSystem, "Sends e-mails using", "SMTP")
Rel(bankingSystem, mainframeBankingSystem, "Gets account information from, and makes payments using", "XML/HTTPS")

@enduml

2. Container (Контейнеры)

Цель: Показать высокоуровневую форму системы.

@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

title Container diagram for Internet Banking System

Person(customer, "Customer", "A customer of the bank, with personal bank accounts.")
Person(customerService, "Customer Service", "Customer service representative of the bank.")

System_Boundary(c1, "Internet Banking System") {
    Container(webApp, "Web Application", "Java, Spring MVC", "Delivers the static content and the Internet banking single page application.")
    Container(api, "API Application", "Java, Spring Boot", "Provides Internet banking functionality via a JSON/HTTPS API.")
    ContainerDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.")
}

System_Ext(emailSystem, "E-mail System", "The internal Microsoft Exchange e-mail system.")
System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")

Rel(customer, webApp, "Uses", "HTTPS")
Rel(customerService, webApp, "Uses", "HTTPS")
Rel(webApp, api, "Makes API calls to", "JSON/HTTPS")
Rel(api, database, "Reads from and writes to", "JDBC")
Rel(api, emailSystem, "Sends e-mails using", "SMTP")
Rel(api, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS")

@enduml

3. Component (Компоненты)

Цель: Показать как контейнер состоит из компонентов.

@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml

title Component diagram for Internet Banking System - API Application

Container_Boundary(api, "API Application") {
    Component(security, "Security Component", "Spring Security", "Provides authentication and authorisation functionality.")
    Component(controllers, "Controllers", "Spring MVC", "Handles HTTP requests and responses.")
    Component(services, "Services", "Spring", "Contains the business logic.")
    Component(repositories, "Repositories", "Spring Data", "Handles data access.")
}

ContainerDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.")
System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")

Rel(controllers, security, "Uses")
Rel(controllers, services, "Uses")
Rel(services, repositories, "Uses")
Rel(repositories, database, "Reads from and writes to", "JDBC")
Rel(services, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS")

@enduml

4. Code (Код)

Цель: Показать детали реализации компонентов.

@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml

title Component diagram for Internet Banking System - Services

Component_Boundary(services, "Services") {
    Component(accountService, "AccountService", "Java Class", "Provides account related operations.")
    Component(securityService, "SecurityService", "Java Class", "Provides security related operations.")
    Component(emailService, "EmailService", "Java Class", "Provides email related operations.")
}

ComponentDb(database, "Database", "Oracle Database", "Stores user login information, hashed authentication credentials, access logs, etc.")
System_Ext(mainframeBankingSystem, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")

Rel(accountService, database, "Reads from and writes to", "JDBC")
Rel(accountService, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS")
Rel(securityService, database, "Reads from and writes to", "JDBC")
Rel(emailService, mainframeBankingSystem, "Makes API calls to", "XML/HTTPS")

@enduml

Лучшие практики C4 Model

1. Используйте правильный уровень детализации

  • Context: для стейкхолдеров
  • Container: для разработчиков
  • Component: для архитекторов
  • Code: для разработчиков

2. Фокусируйтесь на взаимодействиях

  • Показывайте потоки данных
  • Указывайте протоколы
  • Описывайте интерфейсы

3. Обновляйте документацию

  • Синхронизируйте с кодом
  • Версионируйте изменения
  • Отслеживайте устаревшую документацию

PlantUML

Что такое PlantUML

PlantUML — инструмент для создания диаграмм из текстового описания.

Типы диаграмм

1. Sequence Diagrams (Диаграммы последовательности)

@startuml
title Order Processing Sequence

actor Customer
participant "Web App" as Web
participant "API Gateway" as Gateway
participant "Order Service" as Order
participant "Payment Service" as Payment
participant "Inventory Service" as Inventory
database "Database" as DB

Customer -> Web: Create Order
Web -> Gateway: POST /api/orders
Gateway -> Order: Create Order Request
Order -> Inventory: Check Availability
Inventory --> Order: Availability Status
Order -> Payment: Process Payment
Payment --> Order: Payment Result
Order -> DB: Save Order
DB --> Order: Order Saved
Order --> Gateway: Order Created
Gateway --> Web: Order Response
Web --> Customer: Order Confirmation
@enduml

2. Class Diagrams (Диаграммы классов)

@startuml
title Order Domain Model

class Order {
    - id: OrderId
    - customerId: CustomerId
    - items: List<OrderItem>
    - status: OrderStatus
    - totalAmount: Money
    - createdAt: LocalDateTime
    + addItem(item: OrderItem): void
    + removeItem(itemId: ItemId): void
    + confirm(): void
    + cancel(): void
}

class OrderItem {
    - id: ItemId
    - productId: ProductId
    - quantity: int
    - unitPrice: Money
    + getTotalPrice(): Money
}

class Money {
    - amount: BigDecimal
    - currency: Currency
    + add(other: Money): Money
    + multiply(factor: BigDecimal): Money
}

Order ||--o{ OrderItem : contains
OrderItem ||--|| Money : unitPrice
Order ||--|| Money : totalAmount

@enduml

3. Activity Diagrams (Диаграммы активности)

@startuml
title Order Processing Flow

start
:Receive Order Request;
:Validate Order Data;
if (Data Valid?) then (yes)
    :Check Inventory;
    if (Items Available?) then (yes)
        :Reserve Items;
        :Process Payment;
        if (Payment Successful?) then (yes)
            :Create Order;
            :Send Confirmation;
            stop
        else (no)
            :Release Reserved Items;
            :Return Payment Error;
            stop
        endif
    else (no)
        :Return Inventory Error;
        stop
    endif
else (no)
    :Return Validation Error;
    stop
endif
@enduml

4. Deployment Diagrams (Диаграммы развертывания)

@startuml
title System Deployment

node "Load Balancer" {
    component "NGINX" as nginx
}

node "Application Servers" {
    component "API Gateway" as gateway
    component "Order Service" as order
    component "Payment Service" as payment
    component "User Service" as user
}

node "Database Servers" {
    database "PostgreSQL" as db
    database "Redis" as cache
}

node "External Services" {
    component "Payment Gateway" as ext_payment
    component "Email Service" as ext_email
}

nginx --> gateway
gateway --> order
gateway --> payment
gateway --> user
order --> db
payment --> db
user --> db
order --> cache
payment --> cache
user --> cache
payment --> ext_payment
user --> ext_email

@enduml

Лучшие практики PlantUML

1. Используйте семантические имена

# Хорошо
participant "Order Service" as OrderService
participant "Payment Service" as PaymentService

# Плохо
participant "Service1" as S1
participant "Service2" as S2

2. Группируйте связанные элементы

box "Order Processing" #LightBlue
    participant "Order Service" as Order
    participant "Inventory Service" as Inventory
end box

box "Payment Processing" #LightGreen
    participant "Payment Service" as Payment
    participant "Payment Gateway" as Gateway
end box

3. Используйте цвета для выделения

actor Customer #LightBlue
participant "Web App" as Web #LightGreen
participant "API Gateway" as Gateway #LightYellow
database "Database" as DB #LightCoral

Architecture Decision Records (ADR)

Что такое ADR

Architecture Decision Record (ADR) — документ, который фиксирует важное архитектурное решение, принятое в проекте.

Структура ADR

1. Статус

  • Proposed — предложено
  • Accepted — принято
  • Deprecated — устарело
  • Superseded — заменено

2. Контекст

  • Описание ситуации
  • Проблема, которую нужно решить
  • Ограничения и требования

3. Решение

  • Описание принятого решения
  • Обоснование выбора
  • Альтернативы, которые рассматривались

4. Последствия

  • Положительные последствия
  • Отрицательные последствия
  • Риски и митигации

Пример ADR

Технический пример - Advanced ADR Management:

# ADR-001: Использование микросервисной архитектуры

## Статус
Принято

## Дата
2024-01-15

## Контекст
Наша система электронной коммерции растет и становится сложнее. Монолитная архитектура создает следующие проблемы:

- Сложность развертывания изменений
- Технологическая привязка к одному стеку
- Сложность масштабирования отдельных компонентов
- Риск каскадных отказов

Команда разработки увеличилась до 15 человек, и мы планируем дальнейший рост.

## Решение
Мы решили перейти к микросервисной архитектуре со следующими принципами:

- Каждый сервис отвечает за один бизнес-домен
- Сервисы независимо развертываются
- Сервисы имеют собственные базы данных
- Коммуникация через REST API и события

## Альтернативы
1. **Модульный монолит** — разделение на модули внутри одного приложения
2. **SOA** — сервисно-ориентированная архитектура
3. **Оставить монолит** — продолжить развитие текущей архитектуры

## Последствия

### Положительные
- Независимое развертывание сервисов
- Возможность использования разных технологий
- Лучшая масштабируемость
- Изоляция отказов

### Отрицательные
- Увеличение сложности системы
- Проблемы с распределенными транзакциями
- Сложность мониторинга
- Overhead коммуникации

### Риски
- **Сложность отладки** — митигация через distributed tracing
- **Проблемы с данными** — митигация через event sourcing
- **Сетевые задержки** — митигация через кэширование

## Реализация
- Начать с выделения User Service
- Использовать API Gateway для маршрутизации
- Настроить мониторинг и логирование
- Постепенно выделять остальные сервисы

## Метрики успеха
- Время развертывания: < 5 минут
- Время восстановления: < 30 минут
- Доступность: > 99.9%
- Производительность: P95 < 200ms

Технический пример - ADR Management System:

// ADR Management Service
@Service
public class ADRManagementService {
    
    @Autowired
    private ADRRepository adrRepository;
    
    @Autowired
    private NotificationService notificationService;
    
    @Autowired
    private VersionControlService versionControlService;
    
    public ADR createADR(CreateADRRequest request) {
        // Валидация
        validateADRRequest(request);
        
        // Создание ADR
        ADR adr = ADR.builder()
            .id(generateADRId())
            .title(request.getTitle())
            .status(ADRStatus.PROPOSED)
            .context(request.getContext())
            .decision(request.getDecision())
            .alternatives(request.getAlternatives())
            .consequences(request.getConsequences())
            .author(request.getAuthor())
            .createdDate(LocalDateTime.now())
            .build();
        
        // Сохранение в БД
        adr = adrRepository.save(adr);
        
        // Создание файла в Git
        createADRFile(adr);
        
        // Уведомление команды
        notifyTeamAboutNewADR(adr);
        
        return adr;
    }
    
    public ADR updateADRStatus(Long adrId, ADRStatus newStatus, String comment) {
        ADR adr = adrRepository.findById(adrId)
            .orElseThrow(() -> new ADRNotFoundException("ADR not found"));
        
        // Проверяем права на изменение статуса
        validateStatusChangePermission(adr, newStatus);
        
        // Обновляем статус
        adr.setStatus(newStatus);
        adr.setLastModified(LocalDateTime.now());
        adr.setStatusComment(comment);
        
        adr = adrRepository.save(adr);
        
        // Обновляем файл в Git
        updateADRFile(adr);
        
        // Уведомляем команду об изменении
        notifyTeamAboutStatusChange(adr);
        
        return adr;
    }
    
    public List<ADR> getADRsByStatus(ADRStatus status) {
        return adrRepository.findByStatus(status);
    }
    
    public List<ADR> getADRsByDomain(String domain) {
        return adrRepository.findByDomain(domain);
    }
    
    public ADRImpactAnalysis analyzeImpact(Long adrId) {
        ADR adr = adrRepository.findById(adrId)
            .orElseThrow(() -> new ADRNotFoundException("ADR not found"));
        
        return ADRImpactAnalysis.builder()
            .affectedServices(analyzeAffectedServices(adr))
            .requiredChanges(analyzeRequiredChanges(adr))
            .estimatedEffort(estimateEffort(adr))
            .risks(identifyRisks(adr))
            .build();
    }
    
    @Scheduled(cron = "0 0 9 * * MON") // Каждый понедельник в 9:00
    public void reviewADRs() {
        List<ADR> proposedADRs = getADRsByStatus(ADRStatus.PROPOSED);
        
        for (ADR adr : proposedADRs) {
            if (isADRStale(adr)) {
                notifyTeamAboutStaleADR(adr);
            }
        }
        
        // Генерируем отчет по ADR
        generateADRReport();
    }
    
    private void createADRFile(ADR adr) {
        String fileName = String.format("adr/%03d-%s.md", adr.getId(), 
            adr.getTitle().toLowerCase().replaceAll("\\s+", "-"));
        
        String content = generateADRMarkdown(adr);
        
        versionControlService.createFile(fileName, content, 
            String.format("Add ADR-%d: %s", adr.getId(), adr.getTitle()));
    }
    
    private String generateADRMarkdown(ADR adr) {
        StringBuilder markdown = new StringBuilder();
        
        markdown.append(String.format("# ADR-%03d: %s\n\n", adr.getId(), adr.getTitle()));
        markdown.append("## Статус\n");
        markdown.append(adr.getStatus().getDisplayName()).append("\n\n");
        markdown.append("## Дата\n");
        markdown.append(adr.getCreatedDate().toLocalDate()).append("\n\n");
        markdown.append("## Автор\n");
        markdown.append(adr.getAuthor()).append("\n\n");
        markdown.append("## Контекст\n");
        markdown.append(adr.getContext()).append("\n\n");
        markdown.append("## Решение\n");
        markdown.append(adr.getDecision()).append("\n\n");
        
        if (adr.getAlternatives() != null && !adr.getAlternatives().isEmpty()) {
            markdown.append("## Альтернативы\n");
            for (String alternative : adr.getAlternatives()) {
                markdown.append("- ").append(alternative).append("\n");
            }
            markdown.append("\n");
        }
        
        markdown.append("## Последствия\n");
        markdown.append(adr.getConsequences()).append("\n\n");
        
        if (adr.getStatusComment() != null) {
            markdown.append("## Комментарии\n");
            markdown.append(adr.getStatusComment()).append("\n\n");
        }
        
        return markdown.toString();
    }
    
    private boolean isADRStale(ADR adr) {
        return adr.getCreatedDate().isBefore(LocalDateTime.now().minusWeeks(2));
    }
    
    private void generateADRReport() {
        List<ADR> allADRs = adrRepository.findAll();
        
        ADRReport report = ADRReport.builder()
            .totalADRs(allADRs.size())
            .proposedADRs(countByStatus(allADRs, ADRStatus.PROPOSED))
            .acceptedADRs(countByStatus(allADRs, ADRStatus.ACCEPTED))
            .deprecatedADRs(countByStatus(allADRs, ADRStatus.DEPRECATED))
            .supersededADRs(countByStatus(allADRs, ADRStatus.SUPERSEDED))
            .recentADRs(getRecentADRs(allADRs, 30))
            .staleADRs(getStaleADRs(allADRs))
            .build();
        
        String reportContent = generateReportMarkdown(report);
        writeToFile("docs/adr-report.md", reportContent);
        
        // Отправляем отчет команде
        notificationService.sendReport("ADR Report", reportContent);
    }
}

// ADR Entity
@Entity
@Table(name = "architecture_decision_records")
public class ADR {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String title;
    
    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private ADRStatus status;
    
    @Column(columnDefinition = "TEXT")
    private String context;
    
    @Column(columnDefinition = "TEXT")
    private String decision;
    
    @ElementCollection
    @CollectionTable(name = "adr_alternatives", joinColumns = @JoinColumn(name = "adr_id"))
    @Column(name = "alternative", columnDefinition = "TEXT")
    private List<String> alternatives;
    
    @Column(columnDefinition = "TEXT")
    private String consequences;
    
    @Column(nullable = false)
    private String author;
    
    @Column(nullable = false)
    private LocalDateTime createdDate;
    
    private LocalDateTime lastModified;
    
    private String statusComment;
    
    private String domain;
    
    // Getters and setters
}

// ADR Status Enum
public enum ADRStatus {
    PROPOSED("Предложено"),
    ACCEPTED("Принято"),
    DEPRECATED("Устарело"),
    SUPERSEDED("Заменено");
    
    private final String displayName;
    
    ADRStatus(String displayName) {
        this.displayName = displayName;
    }
    
    public String getDisplayName() {
        return displayName;
    }
}

// ADR Impact Analysis
public class ADRImpactAnalysis {
    private List<String> affectedServices;
    private List<String> requiredChanges;
    private String estimatedEffort;
    private List<String> risks;
    
    // Builder pattern implementation
    public static Builder builder() {
        return new Builder();
    }
    
    public static class Builder {
        private List<String> affectedServices = new ArrayList<>();
        private List<String> requiredChanges = new ArrayList<>();
        private String estimatedEffort;
        private List<String> risks = new ArrayList<>();
        
        public Builder affectedServices(List<String> affectedServices) {
            this.affectedServices = affectedServices;
            return this;
        }
        
        public Builder requiredChanges(List<String> requiredChanges) {
            this.requiredChanges = requiredChanges;
            return this;
        }
        
        public Builder estimatedEffort(String estimatedEffort) {
            this.estimatedEffort = estimatedEffort;
            return this;
        }
        
        public Builder risks(List<String> risks) {
            this.risks = risks;
            return this;
        }
        
        public ADRImpactAnalysis build() {
            ADRImpactAnalysis analysis = new ADRImpactAnalysis();
            analysis.affectedServices = this.affectedServices;
            analysis.requiredChanges = this.requiredChanges;
            analysis.estimatedEffort = this.estimatedEffort;
            analysis.risks = this.risks;
            return analysis;
        }
    }
}

// ADR Repository
@Repository
public interface ADRRepository extends JpaRepository<ADR, Long> {
    
    List<ADR> findByStatus(ADRStatus status);
    
    List<ADR> findByDomain(String domain);
    
    List<ADR> findByAuthor(String author);
    
    List<ADR> findByCreatedDateBetween(LocalDateTime start, LocalDateTime end);
    
    @Query("SELECT a FROM ADR a WHERE a.status = 'PROPOSED' AND a.createdDate < :cutoffDate")
    List<ADR> findStaleProposedADRs(@Param("cutoffDate") LocalDateTime cutoffDate);
    
    @Query("SELECT a FROM ADR a ORDER BY a.createdDate DESC")
    List<ADR> findRecentADRs(Pageable pageable);
}

Node.js примеры - ADR Management System:

import express, { Request, Response, NextFunction } from 'express';
import { PrismaClient } from '@prisma/client';
import fs from 'fs/promises';
import path from 'path';
import { z } from 'zod';

const app = express();
const prisma = new PrismaClient();

// ADR Management Service
class ADRManagementService {
  constructor(
    private adrRepository: ADRRepository,
    private notificationService: NotificationService,
    private versionControlService: VersionControlService
  ) {}

  async createADR(request: CreateADRRequest): Promise<ADR> {
    this.validateADRRequest(request);

    // Создание ADR
    const adr = await this.adrRepository.save({
      id: await this.generateADRId(),
      title: request.title,
      domain: request.domain,
      author: request.author,
      status: ADRStatus.PROPOSED,
      context: request.context,
      decision: request.decision,
      alternatives: request.alternatives,
      consequences: request.consequences,
      implementation: request.implementation,
      createdDate: new Date(),
      updatedDate: new Date()
    });

    // Создание файла ADR
    await this.createADRFile(adr);

    // Интеграция с системой контроля версий
    await this.versionControlService.commitADR(adr);

    // Уведомление команды
    await this.notificationService.notifyTeamAboutNewADR(adr);

    return adr;
  }

  async updateADRStatus(adrId: number, newStatus: ADRStatus, comment: string): Promise<ADR> {
    const adr = await this.adrRepository.findById(adrId);
    if (!adr) {
      throw new Error('ADR not found');
    }

    // Обновление статуса
    adr.status = newStatus;
    adr.updatedDate = new Date();
    if (comment) {
      adr.comments = adr.comments || [];
      adr.comments.push({
        author: 'System',
        content: comment,
        timestamp: new Date()
      });
    }

    const updatedADR = await this.adrRepository.save(adr);

    // Обновление файла ADR
    await this.updateADRFile(updatedADR);

    // Уведомление команды об изменении
    await this.notificationService.notifyTeamAboutADRUpdate(updatedADR);

    return updatedADR;
  }

  async getADRsByStatus(status: ADRStatus): Promise<ADR[]> {
    return await this.adrRepository.findByStatus(status);
  }

  async getADRsByDomain(domain: string): Promise<ADR[]> {
    return await this.adrRepository.findByDomain(domain);
  }

  async analyzeImpact(adrId: number): Promise<ADRImpactAnalysis> {
    const adr = await this.adrRepository.findById(adrId);
    if (!adr) {
      throw new Error('ADR not found');
    }

    return {
      adrId: adr.id,
      affectedServices: await this.getAffectedServices(adr),
      estimatedEffort: await this.estimateEffort(adr),
      risks: await this.identifyRisks(adr),
      dependencies: await this.getDependencies(adr),
      timeline: await this.estimateTimeline(adr)
    };
  }

  @Scheduled('0 0 9 * * MON') // Каждый понедельник в 9:00
  async reviewADRs(): Promise<void> {
    const proposedADRs = await this.getADRsByStatus(ADRStatus.PROPOSED);

    for (const adr of proposedADRs) {
      if (this.isADRStale(adr)) {
        await this.notificationService.notifyTeamAboutStaleADR(adr);
      }
    }

    // Генерируем отчет по ADR
    await this.generateADRReport();
  }

  private async createADRFile(adr: ADR): Promise<void> {
    const adrDir = path.join(process.cwd(), 'docs', 'adr');
    await fs.mkdir(adrDir, { recursive: true });

    const content = this.generateADRMarkdown(adr);
    const filePath = path.join(adrDir, `adr-${adr.id.toString().padStart(3, '0')}.md`);

    await fs.writeFile(filePath, content, 'utf8');
  }

  private generateADRMarkdown(adr: ADR): string {
    const markdown: string[] = [];
    
    markdown.push(`# ADR-${adr.id.toString().padStart(3, '0')}: ${adr.title}\n`);
    markdown.push(`## Статус\n${adr.status}\n`);
    markdown.push(`## Дата\n${adr.createdDate.toISOString().split('T')[0]}\n`);
    markdown.push(`## Автор\n${adr.author}\n`);
    markdown.push(`## Домен\n${adr.domain}\n`);
    markdown.push(`## Контекст\n${adr.context}\n`);
    markdown.push(`## Решение\n${adr.decision}\n`);
    
    if (adr.alternatives) {
      markdown.push(`## Альтернативы\n${adr.alternatives}\n`);
    }
    
    if (adr.consequences) {
      markdown.push(`## Последствия\n${adr.consequences}\n`);
    }
    
    if (adr.implementation) {
      markdown.push(`## Реализация\n${adr.implementation}\n`);
    }

    return markdown.join('');
  }

  private async updateADRFile(adr: ADR): Promise<void> {
    const filePath = path.join(process.cwd(), 'docs', 'adr', `adr-${adr.id.toString().padStart(3, '0')}.md`);
    const content = this.generateADRMarkdown(adr);
    await fs.writeFile(filePath, content, 'utf8');
  }

  private isADRStale(adr: ADR): boolean {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - 14); // 2 недели
    return adr.createdDate < cutoffDate;
  }

  private async generateADRReport(): Promise<void> {
    const allADRs = await this.adrRepository.findAll();
    
    const report = {
      totalADRs: allADRs.length,
      proposedADRs: allADRs.filter(adr => adr.status === ADRStatus.PROPOSED).length,
      acceptedADRs: allADRs.filter(adr => adr.status === ADRStatus.ACCEPTED).length,
      deprecatedADRs: allADRs.filter(adr => adr.status === ADRStatus.DEPRECATED).length,
      supersededADRs: allADRs.filter(adr => adr.status === ADRStatus.SUPERSEDED).length,
      recentADRs: this.getRecentADRs(allADRs, 30),
      staleADRs: this.getStaleADRs(allADRs)
    };

    const reportContent = this.formatADRReport(report);
    await this.notificationService.sendReport('ADR Report', reportContent);
  }

  private getRecentADRs(adrs: ADR[], days: number): ADR[] {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - days);
    return adrs.filter(adr => adr.createdDate >= cutoffDate);
  }

  private getStaleADRs(adrs: ADR[]): ADR[] {
    return adrs.filter(adr => this.isADRStale(adr));
  }

  private formatADRReport(report: any): string {
    return `
# ADR Report - ${new Date().toISOString().split('T')[0]}

## Summary
- Total ADRs: ${report.totalADRs}
- Proposed: ${report.proposedADRs}
- Accepted: ${report.acceptedADRs}
- Deprecated: ${report.deprecatedADRs}
- Superseded: ${report.supersededADRs}

## Recent ADRs (last 30 days)
${report.recentADRs.map(adr => `- ADR-${adr.id}: ${adr.title}`).join('\n')}

## Stale ADRs (older than 14 days)
${report.staleADRs.map(adr => `- ADR-${adr.id}: ${adr.title}`).join('\n')}
    `;
  }

  private validateADRRequest(request: CreateADRRequest): void {
    if (!request.title || !request.title.trim()) {
      throw new Error('Title is required');
    }
    if (!request.domain || !request.domain.trim()) {
      throw new Error('Domain is required');
    }
    if (!request.author || !request.author.trim()) {
      throw new Error('Author is required');
    }
    if (!request.context || !request.context.trim()) {
      throw new Error('Context is required');
    }
    if (!request.decision || !request.decision.trim()) {
      throw new Error('Decision is required');
    }
  }

  private async generateADRId(): Promise<number> {
    const lastADR = await this.adrRepository.findLast();
    return (lastADR?.id || 0) + 1;
  }

  private async getAffectedServices(adr: ADR): Promise<string[]> {
    // Логика определения затронутых сервисов
    return ['user-service', 'order-service', 'payment-service'];
  }

  private async estimateEffort(adr: ADR): Promise<string> {
    // Логика оценки усилий
    return 'Medium (2-4 weeks)';
  }

  private async identifyRisks(adr: ADR): Promise<string[]> {
    // Логика идентификации рисков
    return ['Breaking changes', 'Performance impact', 'Data migration'];
  }

  private async getDependencies(adr: ADR): Promise<string[]> {
    // Логика определения зависимостей
    return ['Database schema changes', 'API updates', 'Infrastructure changes'];
  }

  private async estimateTimeline(adr: ADR): Promise<string> {
    // Логика оценки временных рамок
    return 'Q2 2024';
  }
}

// ADR Entity
interface ADR {
  id: number;
  title: string;
  domain: string;
  author: string;
  status: ADRStatus;
  context: string;
  decision: string;
  alternatives?: string;
  consequences?: string;
  implementation?: string;
  comments?: ADRComment[];
  createdDate: Date;
  updatedDate: Date;
}

interface ADRComment {
  author: string;
  content: string;
  timestamp: Date;
}

// ADR Status Enum
enum ADRStatus {
  PROPOSED = 'PROPOSED',
  ACCEPTED = 'ACCEPTED',
  DEPRECATED = 'DEPRECATED',
  SUPERSEDED = 'SUPERSEDED'
}

// ADR Impact Analysis
interface ADRImpactAnalysis {
  adrId: number;
  affectedServices: string[];
  estimatedEffort: string;
  risks: string[];
  dependencies: string[];
  timeline: string;
}

// Repository
interface ADRRepository {
  save(adr: Omit<ADR, 'id'>): Promise<ADR>;
  findById(id: number): Promise<ADR | null>;
  findByStatus(status: ADRStatus): Promise<ADR[]>;
  findByDomain(domain: string): Promise<ADR[]>;
  findAll(): Promise<ADR[]>;
  findLast(): Promise<ADR | null>;
}

class PrismaADRRepository implements ADRRepository {
  async save(adr: Omit<ADR, 'id'>): Promise<ADR> {
    return await prisma.adr.create({ data: adr });
  }

  async findById(id: number): Promise<ADR | null> {
    return await prisma.adr.findUnique({ where: { id } });
  }

  async findByStatus(status: ADRStatus): Promise<ADR[]> {
    return await prisma.adr.findMany({ where: { status } });
  }

  async findByDomain(domain: string): Promise<ADR[]> {
    return await prisma.adr.findMany({ where: { domain } });
  }

  async findAll(): Promise<ADR[]> {
    return await prisma.adr.findMany();
  }

  async findLast(): Promise<ADR | null> {
    return await prisma.adr.findFirst({
      orderBy: { id: 'desc' }
    });
  }
}

// Services
class NotificationService {
  async notifyTeamAboutNewADR(adr: ADR): Promise<void> {
    console.log(`New ADR created: ADR-${adr.id} - ${adr.title}`);
    // Реализация уведомлений (email, Slack, etc.)
  }

  async notifyTeamAboutADRUpdate(adr: ADR): Promise<void> {
    console.log(`ADR updated: ADR-${adr.id} - Status: ${adr.status}`);
  }

  async notifyTeamAboutStaleADR(adr: ADR): Promise<void> {
    console.log(`Stale ADR detected: ADR-${adr.id} - ${adr.title}`);
  }

  async sendReport(title: string, content: string): Promise<void> {
    console.log(`Report: ${title}\n${content}`);
  }
}

class VersionControlService {
  async commitADR(adr: ADR): Promise<void> {
    console.log(`Committing ADR-${adr.id} to version control`);
    // Интеграция с Git
  }
}

// Validation schemas
const createADRSchema = z.object({
  title: z.string().min(1, 'Title is required'),
  domain: z.string().min(1, 'Domain is required'),
  author: z.string().min(1, 'Author is required'),
  context: z.string().min(1, 'Context is required'),
  decision: z.string().min(1, 'Decision is required'),
  alternatives: z.string().optional(),
  consequences: z.string().optional(),
  implementation: z.string().optional()
});

// Dependency injection
const adrRepository = new PrismaADRRepository();
const notificationService = new NotificationService();
const versionControlService = new VersionControlService();
const adrManagementService = new ADRManagementService(
  adrRepository,
  notificationService,
  versionControlService
);

// API endpoints
app.post('/api/adr', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const validatedData = createADRSchema.parse(req.body);
    const adr = await adrManagementService.createADR(validatedData);
    res.json(adr);
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({ error: error.errors });
    } else {
      next(error);
    }
  }
});

app.put('/api/adr/:id/status', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const id = parseInt(req.params.id);
    const { status, comment } = req.body;
    const adr = await adrManagementService.updateADRStatus(id, status, comment);
    res.json(adr);
  } catch (error) {
    next(error);
  }
});

app.get('/api/adr/status/:status', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const status = req.params.status as ADRStatus;
    const adrs = await adrManagementService.getADRsByStatus(status);
    res.json(adrs);
  } catch (error) {
    next(error);
  }
});

app.get('/api/adr/domain/:domain', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const domain = req.params.domain;
    const adrs = await adrManagementService.getADRsByDomain(domain);
    res.json(adrs);
  } catch (error) {
    next(error);
  }
});

app.get('/api/adr/:id/impact', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const id = parseInt(req.params.id);
    const analysis = await adrManagementService.analyzeImpact(id);
    res.json(analysis);
  } catch (error) {
    next(error);
  }
});

// Error handling middleware
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(error);
  res.status(500).json({ error: 'Internal server error' });
});

app.listen(3000, () => {
  console.log('ADR Management Service running on port 3000');
});

Шаблон ADR

# ADR-XXX: [Название решения]

## Статус
[Proposed/Accepted/Deprecated/Superseded]

## Дата
[YYYY-MM-DD]

## Контекст
[Описание ситуации и проблемы]

## Решение
[Описание принятого решения]

## Альтернативы
[Список рассмотренных альтернатив]

## Последствия

### Положительные
[Список положительных последствий]

### Отрицательные
[Список отрицательных последствий]

### Риски
[Список рисков и митигаций]

## Реализация
[План реализации решения]

Техническая документация

Документация API

1. OpenAPI Specification

openapi: 3.0.0
info:
  title: Order Service API
  version: 1.0.0
  description: API for managing orders

servers:
  - url: https://api.example.com/v1
    description: Production server
  - url: https://staging-api.example.com/v1
    description: Staging server

paths:
  /orders:
    post:
      summary: Create a new order
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
      responses:
        '201':
          description: Order created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

components:
  schemas:
    CreateOrderRequest:
      type: object
      required:
        - customerId
        - items
      properties:
        customerId:
          type: string
          format: uuid
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'
    
    Order:
      type: object
      properties:
        id:
          type: string
          format: uuid
        customerId:
          type: string
          format: uuid
        status:
          type: string
          enum: [CREATED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED]
        totalAmount:
          type: number
          format: decimal
        createdAt:
          type: string
          format: date-time

2. API Documentation Tools

  • Swagger UI — интерактивная документация
  • ReDoc — альтернативный интерфейс
  • Postman — коллекции и тесты

Документация базы данных

1. Entity Relationship Diagrams

@startuml
title Order Database Schema

entity "users" {
    * id : UUID
    --
    * username : VARCHAR(50)
    * email : VARCHAR(100)
    * password_hash : VARCHAR(255)
    * created_at : TIMESTAMP
    * updated_at : TIMESTAMP
}

entity "orders" {
    * id : UUID
    --
    * user_id : UUID
    * status : VARCHAR(20)
    * total_amount : DECIMAL(10,2)
    * created_at : TIMESTAMP
    * updated_at : TIMESTAMP
}

entity "order_items" {
    * id : UUID
    --
    * order_id : UUID
    * product_id : UUID
    * quantity : INTEGER
    * unit_price : DECIMAL(10,2)
}

entity "products" {
    * id : UUID
    --
    * name : VARCHAR(100)
    * description : TEXT
    * price : DECIMAL(10,2)
    * stock_quantity : INTEGER
    * created_at : TIMESTAMP
    * updated_at : TIMESTAMP
}

users ||--o{ orders : "places"
orders ||--o{ order_items : "contains"
products ||--o{ order_items : "referenced in"

@enduml

2. Data Dictionary

# Data Dictionary

## Table: users

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | UUID | PRIMARY KEY | Unique identifier for user |
| username | VARCHAR(50) | NOT NULL, UNIQUE | User's login name |
| email | VARCHAR(100) | NOT NULL, UNIQUE | User's email address |
| password_hash | VARCHAR(255) | NOT NULL | Hashed password |
| created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record creation timestamp |
| updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record update timestamp |

## Table: orders

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | UUID | PRIMARY KEY | Unique identifier for order |
| user_id | UUID | NOT NULL, FOREIGN KEY | Reference to users.id |
| status | VARCHAR(20) | NOT NULL | Order status (CREATED, CONFIRMED, etc.) |
| total_amount | DECIMAL(10,2) | NOT NULL | Total order amount |
| created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record creation timestamp |
| updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | Record update timestamp |

Коммуникация архитектуры

Эффективное взаимодействие со стейкхолдерами

1. Типы стейкхолдеров и их потребности

C-Level (CEO, CTO, CFO)

  • Потребности: Бизнес-ценность, ROI, риски, временные рамки
  • Язык: Бизнес-метрики, финансовые показатели, стратегические цели
  • Формат: Executive Summary, высокоуровневые диаграммы
# Executive Summary - Миграция в облако

## Бизнес-обоснование
- **Экономия затрат**: 40% снижение TCO за 3 года
- **Ускорение разработки**: 60% быстрее time-to-market
- **Повышение надежности**: 99.9% uptime vs 95% сейчас

## Инвестиции
- **Начальные затраты**: $150,000 (миграция + обучение)
- **Ежемесячные затраты**: $25,000 (vs $45,000 сейчас)
- **ROI**: 200% за 18 месяцев

## Риски и митигации
- **Риск**: Зависимость от облачного провайдера
- **Митигация**: Multi-cloud стратегия, exit plan
- **Вероятность**: Низкая (5%)

Product Managers

  • Потребности: Функциональность, пользовательский опыт, временные рамки
  • Язык: Пользовательские сценарии, фичи, метрики продукта
  • Формат: User stories, wireframes, API спецификации
# Product Requirements - User Management System

## Пользовательские сценарии
1. **Регистрация пользователя**
   - Пользователь вводит email и пароль
   - Система отправляет подтверждение
   - Пользователь активирует аккаунт

2. **Вход в систему**
   - Пользователь вводит credentials
   - Система аутентифицирует пользователя
   - Пользователь получает доступ к системе

## API Endpoints
- POST /api/users/register
- POST /api/users/login
- GET /api/users/profile
- PUT /api/users/profile

## Метрики успеха
- Время регистрации < 30 секунд
- Успешность входа > 99%
- Время отклика API < 200ms

Development Teams

  • Потребности: Технические детали, API, архитектурные паттерны
  • Язык: Код, диаграммы, технические спецификации
  • Формат: ADR, техническая документация, диаграммы
# Technical Specification - User Service

## Архитектурные решения

### ADR-001: Выбор базы данных
**Статус**: Принято  
**Дата**: 2024-01-15  
**Участники**: Архитектор, Lead Developer, DBA

**Контекст**
Нужно выбрать базу данных для хранения пользовательских данных.

**Варианты**
1. PostgreSQL - реляционная БД
2. MongoDB - документная БД
3. DynamoDB - NoSQL БД

**Решение**
Выбрана PostgreSQL по следующим причинам:
- ACID транзакции для критических данных
- Зрелая экосистема и инструменты
- Опыт команды с SQL
- Хорошая производительность для наших объемов

**Последствия**
- Нужно спроектировать схему БД
- Настроить репликацию для отказоустойчивости
- Реализовать миграции данных

DevOps/Infrastructure Teams

  • Потребности: Инфраструктура, мониторинг, безопасность, производительность
  • Язык: Infrastructure as Code, метрики, алерты
  • Формат: Terraform, Kubernetes manifests, мониторинг дашборды
# Infrastructure Configuration
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  labels:
    app: user-service
    version: v1.0.0
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
        version: v1.0.0
    spec:
      containers:
      - name: user-service
        image: user-service:v1.0.0
        ports:
        - containerPort: 8080
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: user-service-secrets
              key: database-url
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

QA Teams

  • Потребности: Тестируемость, качество, покрытие тестами
  • Язык: Тест-кейсы, сценарии тестирования, метрики качества
  • Формат: Test plans, API тесты, performance тесты
# Test Plan - User Service

## Функциональное тестирование

### Тест-кейсы
1. **TC001: Успешная регистрация пользователя**
   - **Предусловия**: Система доступна
   - **Шаги**: 
     1. Отправить POST /api/users/register с валидными данными
     2. Проверить статус 201
     3. Проверить создание пользователя в БД
   - **Ожидаемый результат**: Пользователь создан, отправлен email

2. **TC002: Регистрация с существующим email**
   - **Предусловия**: Пользователь с email уже существует
   - **Шаги**: 
     1. Отправить POST /api/users/register с существующим email
   - **Ожидаемый результат**: Статус 409, сообщение об ошибке

## Нагрузочное тестирование
- **Цель**: Проверить производительность при 1000 RPS
- **Инструменты**: JMeter, K6
- **Метрики**: Response time < 200ms, Error rate < 0.1%

2. Стратегии коммуникации

1. Адаптация сообщения под аудиторию

# Пример адаптации сообщения
class StakeholderCommunication:
    def __init__(self):
        self.audiences = {
            'executive': {
                'focus': ['business_value', 'roi', 'risks'],
                'language': 'business',
                'detail_level': 'high_level'
            },
            'technical': {
                'focus': ['implementation', 'architecture', 'performance'],
                'language': 'technical',
                'detail_level': 'detailed'
            },
            'product': {
                'focus': ['features', 'user_experience', 'timeline'],
                'language': 'product',
                'detail_level': 'medium'
            }
        }
    
    def create_message(self, audience, topic, data):
        """Создание сообщения для конкретной аудитории"""
        audience_config = self.audiences[audience]
        
        if audience_config['language'] == 'business':
            return self._create_business_message(topic, data)
        elif audience_config['language'] == 'technical':
            return self._create_technical_message(topic, data)
        else:
            return self._create_product_message(topic, data)
    
    def _create_business_message(self, topic, data):
        """Создание бизнес-сообщения"""
        return f"""
        {topic} - Бизнес-обоснование
        
        Ключевые преимущества:
        - Экономия затрат: {data.get('cost_savings', 'N/A')}
        - Ускорение разработки: {data.get('time_savings', 'N/A')}
        - Повышение качества: {data.get('quality_improvement', 'N/A')}
        
        Инвестиции: {data.get('investment', 'N/A')}
        ROI: {data.get('roi', 'N/A')}
        """
    
    def _create_technical_message(self, topic, data):
        """Создание технического сообщения"""
        return f"""
        {topic} - Техническая спецификация
        
        Архитектурные решения:
        - Технологии: {data.get('technologies', 'N/A')}
        - Паттерны: {data.get('patterns', 'N/A')}
        - Производительность: {data.get('performance', 'N/A')}
        
        Реализация:
        - Временные рамки: {data.get('timeline', 'N/A')}
        - Ресурсы: {data.get('resources', 'N/A')}
        - Риски: {data.get('risks', 'N/A')}
        """

2. Визуализация для разных аудиторий

graph TB
    subgraph "Executive View"
        A[Business Goals] --> B[Technical Solution]
        B --> C[Business Value]
        C --> D[ROI]
    end
    
    subgraph "Technical View"
        E[Microservices] --> F[API Gateway]
        F --> G[Database]
        G --> H[Monitoring]
    end
    
    subgraph "Product View"
        I[User Stories] --> J[Features]
        J --> K[User Experience]
        K --> L[Success Metrics]
    end

3. Управление ожиданиями

# Template: Управление ожиданиями

## Проект: Миграция в микросервисы

### Что мы ДЕЛАЕМ:
- ✅ Разбиваем монолит на микросервисы
- ✅ Внедряем API Gateway
- ✅ Настраиваем мониторинг
- ✅ Обеспечиваем отказоустойчивость

### Что мы НЕ ДЕЛАЕМ:
- ❌ Полная переписывание с нуля
- ❌ Изменение бизнес-логики
- ❌ Замена всех технологий

### Ограничения:
- Временные рамки: 6 месяцев
- Бюджет: $500,000
- Команда: 8 разработчиков
- Технологии: Текущий стек + новые инструменты

### Риски:
- Высокий: Сложность миграции данных
- Средний: Производительность в переходный период
- Низкий: Обучение команды новым технологиям

3. Инструменты коммуникации

1. Архитектурные диаграммы

@startuml
!theme plain
skinparam backgroundColor #FFFFFF
skinparam componentStyle rectangle

package "E-commerce Platform" {
    [Web Application] as Web
    [Mobile App] as Mobile
    [Admin Panel] as Admin
    
    package "API Gateway" {
        [Load Balancer] as LB
        [Rate Limiter] as RL
        [Authentication] as Auth
    }
    
    package "Microservices" {
        [User Service] as US
        [Product Service] as PS
        [Order Service] as OS
        [Payment Service] as PayS
    }
    
    package "Data Layer" {
        [User DB] as UDB
        [Product DB] as PDB
        [Order DB] as ODB
        [Payment DB] as PayDB
    }
    
    package "External Services" {
        [Payment Gateway] as PG
        [Email Service] as ES
        [SMS Service] as SMS
    }
}

Web --> LB
Mobile --> LB
Admin --> LB

LB --> RL
RL --> Auth
Auth --> US
Auth --> PS
Auth --> OS
Auth --> PayS

US --> UDB
PS --> PDB
OS --> ODB
PayS --> PayDB

PayS --> PG
US --> ES
US --> SMS

@enduml

2. Презентационные шаблоны

# Шаблон презентации для C-Level

## Slide 1: Executive Summary
- **Проблема**: Текущая система не масштабируется
- **Решение**: Миграция в облако + микросервисы
- **Результат**: 40% экономия + 60% ускорение разработки
- **Инвестиции**: $150K, ROI за 18 месяцев

## Slide 2: Текущее состояние
- Монолитная архитектура
- Высокие затраты на инфраструктуру
- Медленная разработка новых фич
- Проблемы с масштабированием

## Slide 3: Предлагаемое решение
- Микросервисная архитектура
- Облачная инфраструктура
- Автоматизация DevOps
- Современные технологии

## Slide 4: Бизнес-ценность
- **Экономия**: $200K/год
- **Скорость**: 3x быстрее разработка
- **Надежность**: 99.9% uptime
- **Гибкость**: Независимые команды

## Slide 5: План реализации
- **Фаза 1** (2 мес): Подготовка инфраструктуры
- **Фаза 2** (3 мес): Миграция сервисов
- **Фаза 3** (1 мес): Тестирование и оптимизация
- **Фаза 4** (ongoing): Поддержка и развитие

3. Интерактивные демонстрации

<!-- Интерактивная демонстрация архитектуры -->
<!DOCTYPE html>
<html>
<head>
    <title>Architecture Demo</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        .node { cursor: pointer; }
        .link { stroke: #999; stroke-opacity: 0.6; }
        .tooltip { position: absolute; padding: 10px; background: rgba(0,0,0,0.8); color: white; border-radius: 5px; }
    </style>
</head>
<body>
    <div id="architecture-diagram"></div>
    <div id="tooltip" class="tooltip" style="display: none;"></div>
    
    <script>
        // Интерактивная диаграмма архитектуры
        const data = {
            nodes: [
                {id: "web", name: "Web App", type: "frontend"},
                {id: "api", name: "API Gateway", type: "gateway"},
                {id: "user", name: "User Service", type: "microservice"},
                {id: "product", name: "Product Service", type: "microservice"},
                {id: "order", name: "Order Service", type: "microservice"},
                {id: "db", name: "Database", type: "database"}
            ],
            links: [
                {source: "web", target: "api"},
                {source: "api", target: "user"},
                {source: "api", target: "product"},
                {source: "api", target: "order"},
                {source: "user", target: "db"},
                {source: "product", target: "db"},
                {source: "order", target: "db"}
            ]
        };
        
        // Создание интерактивной диаграммы
        const svg = d3.select("#architecture-diagram")
            .append("svg")
            .attr("width", 800)
            .attr("height", 600);
        
        const simulation = d3.forceSimulation(data.nodes)
            .force("link", d3.forceLink(data.links).id(d => d.id))
            .force("charge", d3.forceManyBody().strength(-300))
            .force("center", d3.forceCenter(400, 300));
        
        const link = svg.append("g")
            .selectAll("line")
            .data(data.links)
            .enter().append("line")
            .attr("class", "link");
        
        const node = svg.append("g")
            .selectAll("circle")
            .data(data.nodes)
            .enter().append("circle")
            .attr("class", "node")
            .attr("r", 20)
            .style("fill", d => {
                const colors = {
                    frontend: "#ff6b6b",
                    gateway: "#4ecdc4",
                    microservice: "#45b7d1",
                    database: "#96ceb4"
                };
                return colors[d.type] || "#ddd";
            })
            .on("mouseover", function(event, d) {
                d3.select("#tooltip")
                    .style("display", "block")
                    .html(`<strong>${d.name}</strong><br/>Type: ${d.type}`)
                    .style("left", (event.pageX + 10) + "px")
                    .style("top", (event.pageY - 10) + "px");
            })
            .on("mouseout", function() {
                d3.select("#tooltip").style("display", "none");
            });
        
        simulation.on("tick", () => {
            link
                .attr("x1", d => d.source.x)
                .attr("y1", d => d.source.y)
                .attr("x2", d => d.target.x)
                .attr("y2", d => d.target.y);
            
            node
                .attr("cx", d => d.x)
                .attr("cy", d => d.y);
        });
    </script>
</body>
</html>

4. Управление конфликтами и компромиссами

1. Framework для принятия решений

# Decision Framework

## 1. Определение критериев
- **Функциональность**: Соответствие требованиям
- **Производительность**: Скорость и масштабируемость
- **Безопасность**: Защита данных и системы
- **Стоимость**: Бюджет и ROI
- **Время**: Временные рамки реализации
- **Риски**: Технические и бизнес-риски

## 2. Оценка вариантов
| Критерий | Вариант A | Вариант B | Вариант C | Вес |
|----------|-----------|-----------|-----------|-----|
| Функциональность | 9/10 | 8/10 | 7/10 | 30% |
| Производительность | 7/10 | 9/10 | 8/10 | 25% |
| Безопасность | 8/10 | 7/10 | 9/10 | 20% |
| Стоимость | 6/10 | 8/10 | 9/10 | 15% |
| Время | 8/10 | 6/10 | 7/10 | 10% |
| **Итого** | **7.6** | **7.8** | **7.9** | **100%** |

## 3. Анализ компромиссов
- **Вариант A**: Высокая функциональность, но дорогой
- **Вариант B**: Хорошая производительность, но долгая реализация
- **Вариант C**: Лучшая безопасность, но ограниченная функциональность

## 4. Рекомендация
Выбран **Вариант C** как оптимальный баланс всех критериев.

2. Техники разрешения конфликтов

# Conflict Resolution Framework
class ConflictResolver:
    def __init__(self):
        self.stakeholders = {}
        self.conflicts = []
    
    def identify_conflict(self, stakeholders, issue, positions):
        """Идентификация конфликта"""
        conflict = {
            'id': len(self.conflicts) + 1,
            'stakeholders': stakeholders,
            'issue': issue,
            'positions': positions,
            'status': 'open',
            'resolution': None
        }
        self.conflicts.append(conflict)
        return conflict
    
    def analyze_conflict(self, conflict_id):
        """Анализ конфликта"""
        conflict = self.conflicts[conflict_id - 1]
        
        # Анализ интересов vs позиций
        interests = self._extract_interests(conflict['positions'])
        
        # Поиск общих интересов
        common_interests = self._find_common_interests(interests)
        
        # Генерация вариантов решения
        solutions = self._generate_solutions(conflict, common_interests)
        
        return {
            'interests': interests,
            'common_interests': common_interests,
            'solutions': solutions
        }
    
    def resolve_conflict(self, conflict_id, solution, rationale):
        """Разрешение конфликта"""
        conflict = self.conflicts[conflict_id - 1]
        conflict['resolution'] = {
            'solution': solution,
            'rationale': rationale,
            'status': 'resolved',
            'date': datetime.now()
        }
        return conflict
    
    def _extract_interests(self, positions):
        """Извлечение интересов из позиций"""
        interests = {}
        for stakeholder, position in positions.items():
            interests[stakeholder] = {
                'stated_position': position,
                'underlying_interests': self._analyze_underlying_interests(position)
            }
        return interests
    
    def _find_common_interests(self, interests):
        """Поиск общих интересов"""
        common = []
        stakeholder_interests = list(interests.values())
        
        for i, stakeholder1 in enumerate(stakeholder_interests):
            for j, stakeholder2 in enumerate(stakeholder_interests[i+1:], i+1):
                common_interest = self._compare_interests(
                    stakeholder1['underlying_interests'],
                    stakeholder2['underlying_interests']
                )
                if common_interest:
                    common.append(common_interest)
        
        return common
    
    def _generate_solutions(self, conflict, common_interests):
        """Генерация вариантов решения"""
        solutions = []
        
        # Win-Win решения
        for common_interest in common_interests:
            solutions.append({
                'type': 'win_win',
                'description': f"Решение на основе общего интереса: {common_interest}",
                'benefits': "Удовлетворяет интересы всех сторон",
                'drawbacks': "Может потребовать дополнительных ресурсов"
            })
        
        # Компромиссные решения
        solutions.append({
            'type': 'compromise',
            'description': "Частичное удовлетворение интересов всех сторон",
            'benefits': "Быстрое решение",
            'drawbacks': "Никто не получает полного удовлетворения"
        })
        
        # Альтернативные решения
        solutions.append({
            'type': 'alternative',
            'description': "Поиск третьего варианта",
            'benefits': "Может привести к лучшему решению",
            'drawbacks': "Требует времени и креативности"
        })
        
        return solutions

# Использование
resolver = ConflictResolver()

# Идентификация конфликта
conflict = resolver.identify_conflict(
    stakeholders=['CTO', 'Product Manager', 'Lead Developer'],
    issue='Выбор технологии для нового сервиса',
    positions={
        'CTO': 'Java Spring Boot',
        'Product Manager': 'Node.js для быстрой разработки',
        'Lead Developer': 'Python Django для простоты'
    }
)

# Анализ и разрешение
analysis = resolver.analyze_conflict(conflict['id'])
resolution = resolver.resolve_conflict(
    conflict['id'],
    solution='Использовать Java Spring Boot с быстрыми итерациями',
    rationale='Баланс между производительностью, скоростью разработки и опытом команды'
)

Презентация архитектуры

1. Executive Summary

# Архитектура системы электронной коммерции

## Ключевые метрики
- **Доступность**: 99.9% uptime
- **Производительность**: < 200ms response time
- **Масштабируемость**: до 10,000 RPS
- **Безопасность**: PCI DSS compliant

## Бизнес-ценность
- Быстрое время выхода на рынок
- Независимое развитие команд
- Высокая отказоустойчивость
- Гибкость в выборе технологий

2. Technical Deep Dive

# Техническая архитектура

## Архитектурные принципы
1. **Микросервисы** — независимые сервисы
2. **API-First** — все взаимодействия через API
3. **Event-Driven** — асинхронная обработка
4. **Cloud-Native** — оптимизация для облака

## Технологический стек
- **Backend**: Java, Spring Boot, PostgreSQL
- **Frontend**: React, TypeScript
- **Infrastructure**: Kubernetes, Docker
- **Monitoring**: Prometheus, Grafana

Архитектурные ревью

1. Review Process

# Процесс архитектурного ревью

## Подготовка
1. Создание архитектурной диаграммы
2. Подготовка ADR
3. Анализ рисков и митигаций
4. Планирование миграции

## Проведение ревью
1. Презентация архитектуры (30 мин)
2. Обсуждение и вопросы (30 мин)
3. Анализ рисков (15 мин)
4. Принятие решения (15 мин)

## Результат
- Одобрение или отклонение
- Рекомендации по улучшению
- План реализации
- Следующие шаги

2. Review Checklist

# Чек-лист архитектурного ревью

## Функциональные требования
- [ ] Все требования покрыты
- [ ] Пользовательские сценарии реализуемы
- [ ] Интеграции с внешними системами

## Нефункциональные требования
- [ ] Производительность соответствует требованиям
- [ ] Масштабируемость обеспечена
- [ ] Безопасность учтена
- [ ] Надежность гарантирована

## Техническая реализуемость
- [ ] Технологии подходят для задачи
- [ ] Команда имеет необходимые навыки
- [ ] Инфраструктура поддерживает решение
- [ ] Временные рамки реалистичны

## Риски и митигации
- [ ] Основные риски идентифицированы
- [ ] План митигации разработан
- [ ] Fallback стратегии определены
- [ ] Мониторинг рисков настроен

Практические задания

Задание 1: C4 Model

  1. Создайте Context диаграмму для системы интернет-магазина
  2. Создайте Container диаграмму
  3. Создайте Component диаграмму для одного сервиса

Задание 2: PlantUML

  1. Создайте Sequence диаграмму для процесса заказа
  2. Создайте Class диаграмму для доменной модели
  3. Создайте Deployment диаграмму

Задание 3: ADR

  1. Создайте ADR для выбора базы данных
  2. Создайте ADR для выбора архитектурного стиля
  3. Создайте ADR для стратегии кэширования

Дополнительные ресурсы

Инструменты

  • C4 Model: C4-PlantUML, Structurizr
  • PlantUML: PlantUML Online, VS Code Extension
  • Documentation: GitBook, Confluence, Notion
  • API Docs: Swagger, Postman, Insomnia

Книги

  • "Documenting Software Architectures" by Paul Clements
  • "Architecture Documentation" by Rozanski & Woods
  • "The C4 Model" by Simon Brown

Контрольные вопросы

  1. Зачем нужна архитектурная документация?
  2. Что такое C4 Model и как его использовать?
  3. Какие типы диаграмм можно создать в PlantUML?
  4. Что такое ADR и как его структурировать?
  5. Как документировать API?
  6. Как проводить архитектурные ревью?
  7. Какие принципы архитектурной документации вы знаете?
  8. Как поддерживать актуальность документации?