# Основы архитектурной документации ## Навигация - [🏠 Главная страница курса](../../../README.md) - [📚 Модуль 7](../README.md) --- ## Зачем нужна архитектурная документация ### 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. Deployment Documentation - Схемы развертывания - Конфигурация инфраструктуры - Процедуры развертывания ### 4. Operational Documentation - Мониторинг и логирование - Процедуры восстановления - Руководства по эксплуатации ## Заключение Архитектурная документация является критически важным компонентом успешной разработки: 1. **Коммуникация** — обеспечивает общий язык для команды 2. **Планирование** — основа для принятия решений 3. **Поддержка** — упрощает понимание и поддержку системы 4. **Автоматизация** — современные инструменты позволяют автоматизировать генерацию документации 5. **Живая документация** — должна обновляться вместе с системой Правильная организация и ведение архитектурной документации значительно повышает эффективность команды разработки.