# Модуль 7: Документирование архитектуры ## Содержание модуля 1. [Основы архитектурной документации](#основы-архитектурной-документации) 2. [C4 Model](#c4-model) 3. [PlantUML](#plantuml) 4. [Architecture Decision Records (ADR)](#architecture-decision-records-adr) 5. [Техническая документация](#техническая-документация) 6. [Коммуникация архитектуры](#коммуникация-архитектуры) --- ## Основы архитектурной документации ### Зачем нужна архитектурная документация #### 1. Коммуникация - Общий язык для команды - Понимание системы новыми участниками - Обсуждение архитектурных решений #### 2. Планирование - Основа для принятия решений - Анализ влияния изменений - Планирование миграций #### 3. Поддержка - Быстрое понимание системы - Отладка проблем - Обучение команды ### Принципы архитектурной документации #### 1. Living Documentation **Принцип:** Документация должна быть живой и актуальной. **Технический пример - Living Documentation с автоматизацией:** ```markdown # Архитектура системы 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:** ```java // 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 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 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 getAllServices() { List services = new ArrayList<>(); List serviceNames = discoveryClient.getServices(); for (String serviceName : serviceNames) { List 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 healthResponse = restTemplate.getForEntity(healthUrl, Map.class); String infoUrl = String.format("http://%s:%d/actuator/info", instance.getHost(), instance.getPort()); ResponseEntity 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 info) { Map build = (Map) info.get("build"); return build != null ? build.get("version").toString() : "unknown"; } private List getDependenciesFromInfo(Map info) { // Логика извлечения зависимостей из actuator/info return new ArrayList<>(); } } // Database Schema Analyzer @Component public class DatabaseSchemaAnalyzer { @Autowired private JdbcTemplate jdbcTemplate; public DatabaseSchema analyzeSchema() { List tables = new ArrayList<>(); // Получаем список таблиц List tableNames = getTableNames(); for (String tableName : tableNames) { TableInfo table = analyzeTable(tableName); tables.add(table); } return new DatabaseSchema(tables); } private List 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 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 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 generateSpecifications() { List specifications = new ArrayList<>(); List 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 response = restTemplate.getForEntity(openApiUrl, Map.class); Map openApiSpec = response.getBody(); return ApiSpecification.builder() .serviceName(service.getName()) .version(service.getVersion()) .openApiSpec(openApiSpec) .endpoints(extractEndpoints(openApiSpec)) .build(); } private List extractEndpoints(Map 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 (Контекст) **Цель:** Показать систему в контексте её окружения. ```plantuml @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 (Контейнеры) **Цель:** Показать высокоуровневую форму системы. ```plantuml @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 (Компоненты) **Цель:** Показать как контейнер состоит из компонентов. ```plantuml @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 (Код) **Цель:** Показать детали реализации компонентов. ```plantuml @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 (Диаграммы последовательности) ```plantuml @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 (Диаграммы классов) ```plantuml @startuml title Order Domain Model class Order { - id: OrderId - customerId: CustomerId - items: List - 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 (Диаграммы активности) ```plantuml @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 (Диаграммы развертывания) ```plantuml @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. Используйте семантические имена ```plantuml # Хорошо participant "Order Service" as OrderService participant "Payment Service" as PaymentService # Плохо participant "Service1" as S1 participant "Service2" as S2 ``` #### 2. Группируйте связанные элементы ```plantuml 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. Используйте цвета для выделения ```plantuml 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:** ```markdown # 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:** ```java // 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 getADRsByStatus(ADRStatus status) { return adrRepository.findByStatus(status); } public List 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 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 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 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 affectedServices; private List requiredChanges; private String estimatedEffort; private List risks; // Builder pattern implementation public static Builder builder() { return new Builder(); } public static class Builder { private List affectedServices = new ArrayList<>(); private List requiredChanges = new ArrayList<>(); private String estimatedEffort; private List risks = new ArrayList<>(); public Builder affectedServices(List affectedServices) { this.affectedServices = affectedServices; return this; } public Builder requiredChanges(List requiredChanges) { this.requiredChanges = requiredChanges; return this; } public Builder estimatedEffort(String estimatedEffort) { this.estimatedEffort = estimatedEffort; return this; } public Builder risks(List 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 { List findByStatus(ADRStatus status); List findByDomain(String domain); List findByAuthor(String author); List findByCreatedDateBetween(LocalDateTime start, LocalDateTime end); @Query("SELECT a FROM ADR a WHERE a.status = 'PROPOSED' AND a.createdDate < :cutoffDate") List findStaleProposedADRs(@Param("cutoffDate") LocalDateTime cutoffDate); @Query("SELECT a FROM ADR a ORDER BY a.createdDate DESC") List findRecentADRs(Pageable pageable); } ``` **Node.js примеры - ADR Management System:** ```typescript 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 { 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 { 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 { return await this.adrRepository.findByStatus(status); } async getADRsByDomain(domain: string): Promise { return await this.adrRepository.findByDomain(domain); } async analyzeImpact(adrId: number): Promise { 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 { 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 { 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 { 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 { 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 { const lastADR = await this.adrRepository.findLast(); return (lastADR?.id || 0) + 1; } private async getAffectedServices(adr: ADR): Promise { // Логика определения затронутых сервисов return ['user-service', 'order-service', 'payment-service']; } private async estimateEffort(adr: ADR): Promise { // Логика оценки усилий return 'Medium (2-4 weeks)'; } private async identifyRisks(adr: ADR): Promise { // Логика идентификации рисков return ['Breaking changes', 'Performance impact', 'Data migration']; } private async getDependencies(adr: ADR): Promise { // Логика определения зависимостей return ['Database schema changes', 'API updates', 'Infrastructure changes']; } private async estimateTimeline(adr: ADR): Promise { // Логика оценки временных рамок 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): Promise; findById(id: number): Promise; findByStatus(status: ADRStatus): Promise; findByDomain(domain: string): Promise; findAll(): Promise; findLast(): Promise; } class PrismaADRRepository implements ADRRepository { async save(adr: Omit): Promise { return await prisma.adr.create({ data: adr }); } async findById(id: number): Promise { return await prisma.adr.findUnique({ where: { id } }); } async findByStatus(status: ADRStatus): Promise { return await prisma.adr.findMany({ where: { status } }); } async findByDomain(domain: string): Promise { return await prisma.adr.findMany({ where: { domain } }); } async findAll(): Promise { return await prisma.adr.findMany(); } async findLast(): Promise { return await prisma.adr.findFirst({ orderBy: { id: 'desc' } }); } } // Services class NotificationService { async notifyTeamAboutNewADR(adr: ADR): Promise { console.log(`New ADR created: ADR-${adr.id} - ${adr.title}`); // Реализация уведомлений (email, Slack, etc.) } async notifyTeamAboutADRUpdate(adr: ADR): Promise { console.log(`ADR updated: ADR-${adr.id} - Status: ${adr.status}`); } async notifyTeamAboutStaleADR(adr: ADR): Promise { console.log(`Stale ADR detected: ADR-${adr.id} - ${adr.title}`); } async sendReport(title: string, content: string): Promise { console.log(`Report: ${title}\n${content}`); } } class VersionControlService { async commitADR(adr: ADR): Promise { 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 ```markdown # ADR-XXX: [Название решения] ## Статус [Proposed/Accepted/Deprecated/Superseded] ## Дата [YYYY-MM-DD] ## Контекст [Описание ситуации и проблемы] ## Решение [Описание принятого решения] ## Альтернативы [Список рассмотренных альтернатив] ## Последствия ### Положительные [Список положительных последствий] ### Отрицательные [Список отрицательных последствий] ### Риски [Список рисков и митигаций] ## Реализация [План реализации решения] ``` --- ## Техническая документация ### Документация API #### 1. OpenAPI Specification ```yaml 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 ```plantuml @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 ```markdown # 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, высокоуровневые диаграммы ```markdown # 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 спецификации ```markdown # 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, техническая документация, диаграммы ```markdown # 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, мониторинг дашборды ```yaml # 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 тесты ```markdown # 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. Адаптация сообщения под аудиторию** ```python # Пример адаптации сообщения 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. Визуализация для разных аудиторий** ```mermaid 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. Управление ожиданиями** ```markdown # Template: Управление ожиданиями ## Проект: Миграция в микросервисы ### Что мы ДЕЛАЕМ: - ✅ Разбиваем монолит на микросервисы - ✅ Внедряем API Gateway - ✅ Настраиваем мониторинг - ✅ Обеспечиваем отказоустойчивость ### Что мы НЕ ДЕЛАЕМ: - ❌ Полная переписывание с нуля - ❌ Изменение бизнес-логики - ❌ Замена всех технологий ### Ограничения: - Временные рамки: 6 месяцев - Бюджет: $500,000 - Команда: 8 разработчиков - Технологии: Текущий стек + новые инструменты ### Риски: - Высокий: Сложность миграции данных - Средний: Производительность в переходный период - Низкий: Обучение команды новым технологиям ``` #### 3. Инструменты коммуникации **1. Архитектурные диаграммы** ```plantuml @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. Презентационные шаблоны** ```markdown # Шаблон презентации для 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. Интерактивные демонстрации** ```html Architecture Demo
``` #### 4. Управление конфликтами и компромиссами **1. Framework для принятия решений** ```markdown # 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. Техники разрешения конфликтов** ```python # 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 ```markdown # Архитектура системы электронной коммерции ## Ключевые метрики - **Доступность**: 99.9% uptime - **Производительность**: < 200ms response time - **Масштабируемость**: до 10,000 RPS - **Безопасность**: PCI DSS compliant ## Бизнес-ценность - Быстрое время выхода на рынок - Независимое развитие команд - Высокая отказоустойчивость - Гибкость в выборе технологий ``` #### 2. Technical Deep Dive ```markdown # Техническая архитектура ## Архитектурные принципы 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 ```markdown # Процесс архитектурного ревью ## Подготовка 1. Создание архитектурной диаграммы 2. Подготовка ADR 3. Анализ рисков и митигаций 4. Планирование миграции ## Проведение ревью 1. Презентация архитектуры (30 мин) 2. Обсуждение и вопросы (30 мин) 3. Анализ рисков (15 мин) 4. Принятие решения (15 мин) ## Результат - Одобрение или отклонение - Рекомендации по улучшению - План реализации - Следующие шаги ``` #### 2. Review Checklist ```markdown # Чек-лист архитектурного ревью ## Функциональные требования - [ ] Все требования покрыты - [ ] Пользовательские сценарии реализуемы - [ ] Интеграции с внешними системами ## Нефункциональные требования - [ ] Производительность соответствует требованиям - [ ] Масштабируемость обеспечена - [ ] Безопасность учтена - [ ] Надежность гарантирована ## Техническая реализуемость - [ ] Технологии подходят для задачи - [ ] Команда имеет необходимые навыки - [ ] Инфраструктура поддерживает решение - [ ] Временные рамки реалистичны ## Риски и митигации - [ ] Основные риски идентифицированы - [ ] План митигации разработан - [ ] 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. Как поддерживать актуальность документации?