# 🏗️ Архитектура YandexMusic Полное описание архитектуры, компонентов и взаимодействия между слоями решения YandexMusic. ## 📋 Содержание - [Обзор архитектуры](#обзор-архитектуры) - [Структура слоёв](#структура-слоёв) - [Основные компоненты](#основные-компоненты) - [Потоки данных](#потоки-данных) - [Паттерны проектирования](#паттерны-проектирования) - [Модульная структура](#модульная-структура) - [Расширяемость](#расширяемость) ## 🔍 Обзор архитектуры ### Многослойная архитектура Проект построен на классической трёхслойной архитектуре с элементами DDD: ``` ┌────────────────────────────────────────┐ │ CLI Layer (YaMusicCli) │ ← Точка входа ├────────────────────────────────────────┤ │ Client Layer (YandexMusic) │ ← Удобный интерфейс ├────────────────────────────────────────┤ │ API Layer (YandexMusic.API) │ ← Логика API ├────────────────────────────────────────┤ │ Data Layer (Models) │ ← Данные и сущности ├────────────────────────────────────────┤ │ Infrastructure Layer │ ← HTTP, Serialization └────────────────────────────────────────┘ ``` ### Ключевые принципы - **Separation of Concerns** - каждый слой отвечает за одно - **Dependency Injection** - слабая связанность через интерфейсы - **Single Responsibility** - каждый класс решает одну задачу - **Open/Closed Principle** - открыто для расширения, закрыто для модификации - **Asynchronous First** - всё асинхронное по умолчанию ## 📦 Структура слоёв ### 1. CLI Layer (YaMusicCli) **Назначение:** Командная строка интерфейс для пользователей. ``` YaMusicCli/ ├── Program.cs # Точка входа, парсинг аргументов └── Commands/ # Команды для CLI ├── SearchCommand ├── PlaylistCommand └── TrackCommand ``` **Ответственность:** - Парсинг команд пользователя - Вывод результатов в консоль - Обработка пользовательского ввода - Форматирование данных для вывода **Зависимости:** YandexMusic (Client Layer) ### 2. Client Layer (YandexMusic) **Назначение:** Удобный оборачиватель (wrapper) для работы с API. ``` YandexMusic/ └── YandexMusicClient.cs ├── Initialization (конструктор) ├── Authorization (авторизация) ├── Properties (доступ к API и хранилищу) └── Cleanup (dispose) ``` **Основной класс:** ```csharp public class YandexMusicClient : IDisposable { // Инициализация public YandexMusicClient( CookieContainer? cookieContainer = null, IWebProxy? proxy = null, TimeSpan? timeout = null, string? userAgent = null) // Авторизация public async Task AuthorizeAsync(string login, string password) public async Task AuthorizeByTokenAsync(string token) // Свойства доступа public YandexMusicApi Api { get; } // API Яндекс Музыки public AuthStorage AuthStorage { get; } // Хранилище данных public HttpClient HttpClient { get; } // HttpClient public YAccount Account { get; } // Информация об аккаунте public bool IsAuthorized { get; } // Статус авторизации public YnisonPlayer? Ynison { get; } // WebSocket плеер } ``` **Ответственность:** - Упрощение работы с API - Управление HttpClient и cookies - Обработка авторизации - Инициализация компонентов **Зависимости:** YandexMusic.API (API Layer) ### 3. API Layer (YandexMusic.API) **Назначение:** Низкоуровневой доступ к API Яндекс Музыки. ``` YandexMusic.API/ ├── YandexMusicApi.cs # Главный класс - фасад ├── API/ # API классы по функциям │ ├── YAlbumAPI │ ├── YArtistAPI │ ├── YTrackAPI │ ├── YPlaylistAPI │ ├── YRadioAPI │ ├── YSearchAPI │ └── ... (остальные API) ├── Requests/ # Построители запросов │ ├── Common/ │ ├── Album/ │ ├── Artist/ │ └── ... (по категориям) ├── Models/ # Модели данных │ ├── Common/ │ ├── Album/ │ ├── Track/ │ └── ... (по типам) └── Common/ # Вспомогательные компоненты ├── AuthStorage ├── Providers/ ├── Encryptor └── Ynison/ ``` **Главный класс:** ```csharp public class YandexMusicApi { // API Методы public YAlbumAPI Album { get; } public YArtistAPI Artist { get; } public YTrackAPI Track { get; } public YPlaylistAPI Playlist { get; } public YRadioAPI Radio { get; } public YSearchAPI Search { get; } // ... и так далее для всех веток API } ``` **API Классы:** Каждый API класс наследуется от `YCommonAPI`: ```csharp public abstract class YCommonAPI { protected readonly YandexMusicApi api; protected YCommonAPI(YandexMusicApi yandex) => api = yandex; } // Пример конкретного API public class YTrackAPI : YCommonAPI { public YTrackAPI(YandexMusicApi yandex) : base(yandex) { } public async Task GetTrackAsync(string trackId) { } public async Task> GetTracksAsync(IEnumerable ids) { } // ... остальные методы } ``` **Ответственность:** - Построение HTTP запросов - Вызов провайдера запросов - Десериализация ответов - Обработка ошибок API ### 4. Data Layer (Models) **Назначение:** Модели данных, отражающие структуру API Яндекс Музыки. ``` Models/ ├── Common/ # Общие модели │ ├── YBaseModel # Базовая модель с контекстом │ ├── YResponse # Ответ от API │ ├── YError # Ошибка │ └── ... (остальные) ├── Album/ # Модели альбомов │ ├── YAlbum │ └── YAlbumMenuItem ├── Track/ # Модели треков │ ├── YTrack │ ├── YTrackSupplement │ └── YTrackSimilar ├── Playlist/ # Модели плейлистов │ ├── YPlaylist │ ├── YPlaylistChange │ └── YPlaylistMadeFor └── ... (остальные модели) ``` **Базовая модель:** ```csharp public abstract class YBaseModel { [JsonIgnore] public YExecutionContext? Context { get; set; } } // Пример модели public class YTrack : YBaseModel { [JsonPropertyName("id")] public string Id { get; set; } [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("duration_ms")] public int DurationMs { get; set; } // ... остальные свойства } ``` **Ответственность:** - Представление данных API - System.Text.Json сериализация/десериализация - Хранение контекста выполнения (опционально) ### 5. Infrastructure Layer **Назначение:** Низкоуровневая инфраструктура для работы с HTTP и сериализацией. ``` Common/ ├── Providers/ # Провайдеры запросов │ ├── IRequestProvider # Интерфейс │ ├── DefaultRequestProvider │ ├── CommonRequestProvider │ └── MockRequestProvider ├── AuthStorage # Управление авторизацией ├── Encryptor # Шифрование данных ├── DataDownloader # Загрузка файлов └── Ynison/ # WebSocket ├── YnisonPlayer └── YnisonWebSocket ``` **IRequestProvider:** ```csharp public interface IRequestProvider { // Выполняет HTTP запрос Task GetWebResponseAsync( HttpRequestMessage message, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead); // Преобразует ответ в модель Task GetDataFromResponseAsync( YandexMusicApi api, HttpResponseMessage response); } ``` ## 🔄 Потоки данных ### Типичный поток запроса ``` 1. User Action ↓ 2. YandexMusicClient Method ↓ 3. YandexMusicApi.SomeAPI.SomeMethodAsync() ↓ 4. YRequestBuilder builds HttpRequestMessage ↓ 5. IRequestProvider.GetWebResponseAsync() ↓ 6. HttpClient sends request ↓ 7. Server responds with HttpResponseMessage ↓ 8. IRequestProvider.GetDataFromResponseAsync() ↓ 9. System.Text.Json deserializes to Model ↓ 10. Model returned to caller ``` ### Пример: Получение трека ```csharp // 1. Пользователь вызывает метод var track = await client.Api.Track.GetTrackAsync("trackId123"); // 2. YTrackAPI.GetTrackAsync() создаёт запрос var builder = new YGetTracksBuilder(api); var message = builder.Build("trackId123"); // 3. Запрос отправляется через провайдер var response = await authStorage.Provider.GetWebResponseAsync(message); // 4. Ответ преобразуется в модель var track = await provider.GetDataFromResponseAsync(api, response); // 5. Модель возвращается пользователю return track; ``` ## 🎯 Паттерны проектирования ### 1. Facade Pattern (YandexMusicApi) ```csharp // Фасад предоставляет простой интерфейс к сложной подсистеме public class YandexMusicApi { public YAlbumAPI Album { get; } // Скрывает сложность public YArtistAPI Artist { get; } // инициализации public YTrackAPI Track { get; } // каждого компонента } ``` ### 2. Builder Pattern (Request Builders) ```csharp // Построитель для конструирования запросов public class YSearchBuilder : YRequestBuilder { private string _query; private int _page = 0; public YSearchBuilder Query(string q) { _query = q; return this; } public YSearchBuilder Page(int p) { _page = p; return this; } public override HttpRequestMessage Build() { /* ... */ } } // Использование var search = new YSearchBuilder() .Query("Beatles") .Page(1) .Build(); ``` ### 3. Strategy Pattern (IRequestProvider) ```csharp // Интерфейс позволяет выбрать стратегию обработки запросов public interface IRequestProvider { Task GetWebResponseAsync(HttpRequestMessage message); } // Разные реализации для разных сценариев public class DefaultRequestProvider : IRequestProvider { } public class MockRequestProvider : IRequestProvider { } public class CustomRequestProvider : IRequestProvider { } ``` ### 4. Dependency Injection ```csharp // Зависимости передаются через конструктор public class YTrackAPI : YCommonAPI { protected readonly YandexMusicApi api; // Injected public YTrackAPI(YandexMusicApi yandex) => api = yandex; } ``` ### 5. Template Method Pattern ```csharp // Базовый класс определяет структуру public abstract class YCommonAPI { protected readonly YandexMusicApi api; protected YCommonAPI(YandexMusicApi yandex) => api = yandex; } // Подклассы реализуют специфичные методы public class YAlbumAPI : YCommonAPI { // Реализация методов для альбомов } ``` ## 🧩 Модульная структура ### Логическое разделение на модули ``` Core Module (YandexMusic.API) ├── Authentication Module │ ├── AuthStorage │ └── Authorization methods ├── Request Module │ ├── IRequestProvider │ ├── Request builders │ └── HttpContext ├── Model Module │ └── All data models └── API Module ├── YAlbumAPI ├── YTrackAPI ├── YPlaylistAPI └── ... (each API endpoint) Client Module (YandexMusic) ├── YandexMusicClient └── Client configuration CLI Module (YaMusicCli) ├── Program.cs └── CLI commands ``` ### Зависимости между модулями ``` CLI Module ↓ depends on Client Module ↓ depends on Core Module ├── Authentication Module ├── Request Module ├── Model Module └── API Module ``` ## 🔌 Расширяемость ### Как добавить новый API метод ```csharp // 1. Создайте builder в Requests/YourCategory/ public class YGetYourResourceBuilder : YRequestBuilder { public override HttpRequestMessage Build() { // Реализуйте построение запроса } } // 2. Добавьте метод в соответствующий API класс public class YYourResourceAPI : YCommonAPI { public async Task GetYourResourceAsync(string id) { var builder = new YGetYourResourceBuilder(api); var request = builder.Build(); return await new YRequest(request, api, api.AuthStorage).GetResponseAsync(); } } // 3. Используйте новый метод var resource = await api.YourResource.GetYourResourceAsync("id123"); ``` ### Как создать custom RequestProvider ```csharp public class MyCustomRequestProvider : IRequestProvider { private readonly AuthStorage _storage; public MyCustomRequestProvider(AuthStorage storage) { _storage = storage; } public async Task GetWebResponseAsync( HttpRequestMessage message, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) { // Ваша логика обработки запроса using var handler = new HttpClientHandler(); using var client = new HttpClient(handler); return await client.SendAsync(message, completionOption); } public async Task GetDataFromResponseAsync( YandexMusicApi api, HttpResponseMessage response) { // Ваша логика десериализации var content = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize(content) ?? default!; } } // Использование var provider = new MyCustomRequestProvider(storage); var storage = new AuthStorage(provider); ``` ### Как создать extension method ```csharp public static class YPlaylistExtensions { public static async Task> GetAllTracksAsync( this YPlaylist playlist, YandexMusicApi api) { // Расширение функциональности существующего класса var allTracks = new List(); var currentPage = 0; while (true) { var page = await api.Playlist.GetPlaylistAsync( playlist.Uid, page: currentPage); if (page?.Tracks?.Count == 0) break; allTracks.AddRange(page.Tracks ?? []); currentPage++; } return allTracks; } } ``` ## 📊 Диаграмма взаимодействия компонентов ``` ┌──────────────────┐ │ CLI Layer │ │ (YaMusicCli) │ └────────┬─────────┘ │ ↓ ┌──────────────────────────┐ │ Client Layer │ │ (YandexMusicClient) │ └────────┬─────────────────┘ │ ↓ ┌────────────────────────────────────┐ │ API Layer │ │ ┌─────────────────────────────┐ │ │ │ YandexMusicApi (Facade) │ │ │ └──┬──────────────────────────┘ │ │ │ │ │ ┌──┴─────────┬─────────────────┐│ │ ↓ ↓ ↓│ │ YTrackAPI YPlaylistAPI YSearchAPI │ │ │ │ │ │ │ └──────┬─────┴────────┬────────┘ │ └─────────┼──────────────┼───────────┘ │ │ ↓ ↓ ┌──────────────────────────┐ │ IRequestProvider │ │ (Strategy Pattern) │ └──────┬───────────────────┘ │ ┌──┴───┬──────────┐ ↓ ↓ ↓ Default Common Mock Provider Provider Provider │ │ │ └──────┼────────┘ ↓ ┌──────────────────┐ │ HttpClient │ └──────┬───────────┘ │ ↓ ┌──────────────────┐ │ Network Layer │ └──────────────────┘ ``` ## 🔐 Безопасность архитектуры 1. **Инкапсуляция** - приватные поля и контролируемый доступ 2. **Валидация** - все входные параметры валидируются 3. **Null-safety** - полная поддержка nullable reference types 4. **Async-safe** - асинхронные операции без blocking 5. **Encryption** - встроенное шифрование для чувствительных данных