readme
This commit is contained in:
607
ARCHITECTURE.md
Normal file
607
ARCHITECTURE.md
Normal file
@@ -0,0 +1,607 @@
|
||||
# 🏗️ Архитектура 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** - встроенное шифрование для чувствительных данных
|
||||
Reference in New Issue
Block a user