608 lines
20 KiB
Markdown
608 lines
20 KiB
Markdown
# 🏗️ Архитектура 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<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
|
||
└── ... (остальные модели)
|
||
```
|
||
|
||
**Базовая модель:**
|
||
|
||
```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<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
|
||
```
|
||
|
||
### Пример: Получение трека
|
||
|
||
```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<YTrack>(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<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)
|
||
|
||
```csharp
|
||
// Интерфейс позволяет выбрать стратегию обработки запросов
|
||
public interface IRequestProvider
|
||
{
|
||
Task<HttpResponseMessage> 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<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
|
||
|
||
```csharp
|
||
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
|
||
|
||
```csharp
|
||
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** - встроенное шифрование для чувствительных данных
|