Files
YandexMusic/ARCHITECTURE.md
2026-04-21 12:51:23 +03:00

20 KiB
Raw Permalink Blame History

🏗️ Архитектура 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)

Основной класс:

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/

Главный класс:

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:

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<YTrack?> GetTrackAsync(string trackId) { }
    public async Task<IEnumerable<YTrack>> GetTracksAsync(IEnumerable<string> ids) { }
    // ... остальные методы
}

Ответственность:

  • Построение HTTP запросов
  • Вызов провайдера запросов
  • Десериализация ответов
  • Обработка ошибок API

4. Data Layer (Models)

Назначение: Модели данных, отражающие структуру API Яндекс Музыки.

Models/
├── Common/                  # Общие модели
│   ├── YBaseModel           # Базовая модель с контекстом
│   ├── YResponse<T>         # Ответ от API
│   ├── YError               # Ошибка
│   └── ... (остальные)
├── Album/                   # Модели альбомов
│   ├── YAlbum
│   └── YAlbumMenuItem
├── Track/                   # Модели треков
│   ├── YTrack
│   ├── YTrackSupplement
│   └── YTrackSimilar
├── Playlist/                # Модели плейлистов
│   ├── YPlaylist
│   ├── YPlaylistChange
│   └── YPlaylistMadeFor
└── ... (остальные модели)

Базовая модель:

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:

public interface IRequestProvider
{
    // Выполняет HTTP запрос
    Task<HttpResponseMessage> GetWebResponseAsync(
        HttpRequestMessage message,
        HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead);

    // Преобразует ответ в модель
    Task<T> GetDataFromResponseAsync<T>(
        YandexMusicApi api,
        HttpResponseMessage response);
}

🔄 Потоки данных

Типичный поток запроса

1. User Action
   ↓
2. YandexMusicClient Method
   ↓
3. YandexMusicApi.SomeAPI.SomeMethodAsync()
   ↓
4. YRequestBuilder<T> builds HttpRequestMessage
   ↓
5. IRequestProvider.GetWebResponseAsync()
   ↓
6. HttpClient sends request
   ↓
7. Server responds with HttpResponseMessage
   ↓
8. IRequestProvider.GetDataFromResponseAsync<T>()
   ↓
9. System.Text.Json deserializes to Model<T>
   ↓
10. Model<T> returned to caller

Пример: Получение трека

// 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<YTrack>(api, response);

// 5. Модель возвращается пользователю
return track;

🎯 Паттерны проектирования

1. Facade Pattern (YandexMusicApi)

// Фасад предоставляет простой интерфейс к сложной подсистеме
public class YandexMusicApi
{
    public YAlbumAPI Album { get; }        // Скрывает сложность
    public YArtistAPI Artist { get; }      // инициализации
    public YTrackAPI Track { get; }        // каждого компонента
}

2. Builder Pattern (Request Builders)

// Построитель для конструирования запросов
public class YSearchBuilder : YRequestBuilder<YSearch>
{
    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)

// Интерфейс позволяет выбрать стратегию обработки запросов
public interface IRequestProvider
{
    Task<HttpResponseMessage> GetWebResponseAsync(HttpRequestMessage message);
}

// Разные реализации для разных сценариев
public class DefaultRequestProvider : IRequestProvider { }
public class MockRequestProvider : IRequestProvider { }
public class CustomRequestProvider : IRequestProvider { }

4. Dependency Injection

// Зависимости передаются через конструктор
public class YTrackAPI : YCommonAPI
{
    protected readonly YandexMusicApi api;  // Injected
    
    public YTrackAPI(YandexMusicApi yandex) => api = yandex;
}

5. Template Method Pattern

// Базовый класс определяет структуру
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 метод

// 1. Создайте builder в Requests/YourCategory/
public class YGetYourResourceBuilder : YRequestBuilder<YYourResource>
{
    public override HttpRequestMessage Build()
    {
        // Реализуйте построение запроса
    }
}

// 2. Добавьте метод в соответствующий API класс
public class YYourResourceAPI : YCommonAPI
{
    public async Task<YYourResource?> GetYourResourceAsync(string id)
    {
        var builder = new YGetYourResourceBuilder(api);
        var request = builder.Build();
        return await new YRequest<YYourResource?>(request, api, api.AuthStorage).GetResponseAsync();
    }
}

// 3. Используйте новый метод
var resource = await api.YourResource.GetYourResourceAsync("id123");

Как создать custom RequestProvider

public class MyCustomRequestProvider : IRequestProvider
{
    private readonly AuthStorage _storage;

    public MyCustomRequestProvider(AuthStorage storage)
    {
        _storage = storage;
    }

    public async Task<HttpResponseMessage> 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<T> GetDataFromResponseAsync<T>(
        YandexMusicApi api,
        HttpResponseMessage response)
    {
        // Ваша логика десериализации
        var content = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<T>(content) ?? default!;
    }
}

// Использование
var provider = new MyCustomRequestProvider(storage);
var storage = new AuthStorage(provider);

Как создать extension method

public static class YPlaylistExtensions
{
    public static async Task<IEnumerable<YTrack>> GetAllTracksAsync(
        this YPlaylist playlist,
        YandexMusicApi api)
    {
        // Расширение функциональности существующего класса
        var allTracks = new List<YTrack>();
        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 - встроенное шифрование для чувствительных данных