Какой подход вы используете для организации слоёв в больших кросс-платформенных решениях?
Организация слоёв в больших кросс-платформенных решениях на Xamarin требует строгого соблюдения принципов архитектуры и модульности. Основная цель — добиться высокой масштабируемости, поддержки тестирования, переиспользуемости кода и чёткого разделения ответственности. Подход к организации слоёв зависит от требований проекта, но в большинстве случаев применяется многоуровневая архитектура с использованием MVVM, SOLID-принципов и инверсии зависимостей.
Общий подход к архитектуре слоёв
Стандартная многослойная архитектура включает:
- **Presentation Layer (UI)
** - **Presentation Logic Layer (ViewModels)
** - **Application Layer (UseCases, Services)
** - **Domain Layer (Entities, Interfaces)
** - **Data Layer (Repositories, DataSources)
** - **Infrastructure Layer (Platform-specific code)
**
Подробности по каждому слою
1. Presentation Layer (UI)
Отвечает за отображение интерфейса и взаимодействие с пользователем. В Xamarin.Forms это Pages, Views, Controls.
-
Располагается в платформенном проекте (или Forms UI-проекте)
-
Не содержит бизнес-логики
-
В идеале, только XAML + Code-behind с минимальным кодом (только инициализация)
Пример:
<Button Text="Login" Command="{Binding LoginCommand}" />
2. Presentation Logic Layer (ViewModels)
Содержит логику, управляющую состоянием UI, реализует биндинги, команды и отвечает за реакцию на действия пользователя.
-
Отнаследована от INotifyPropertyChanged или базового ViewModelBase
-
Не содержит кода платформы
-
Использует интерфейсы сервисов (например, IAuthService, INavigationService)
Пример:
public class LoginViewModel : BaseViewModel
{
public ICommand LoginCommand { get; }
public LoginViewModel(IAuthService authService)
{
LoginCommand = new Command(async () => await authService.LoginAsync(Username, Password));
}
}
3. Application Layer
Отвечает за координацию бизнес-логики: использование сценариев (use cases), агрегацию данных, управление транзакциями.
-
Пример классов: LoginUseCase, RegisterUser, GetUserProfile
-
Является посредником между ViewModel и доменной логикой
-
Может реализовывать паттерн CQRS (разделение запросов и команд)
Пример:
public class LoginUseCase
{
private readonly IAuthRepository \_authRepo;
public LoginUseCase(IAuthRepository authRepo)
{
\_authRepo = authRepo;
}
public async Task<Result> Execute(string username, string password)
{
return await \_authRepo.Login(username, password);
}
}
4. Domain Layer
Центральный слой архитектуры. Содержит бизнес-модели, интерфейсы, бизнес-правила, value objects и доменные сервисы.
-
Не зависит ни от чего
-
Можно переиспользовать в других проектах (например, в MAUI, Blazor, ASP.NET)
-
Интерфейсы для репозиториев, сервисов, провайдеров
Пример:
public class User
{
public string Username { get; set; }
public bool IsAdmin { get; set; }
public bool CanEditProfile() => IsAdmin;
}
5. Data Layer
Реализация интерфейсов репозиториев и подключение к источникам данных:
-
REST API (через HttpClient)
-
SQLite (через SQLite-net)
-
SecureStorage, Preferences, FileSystem
-
Использует DTO, Mappers и Models
Пример:
public class AuthRepository : IAuthRepository
{
private readonly IApiClient \_client;
public async Task<Result> Login(string username, string password)
{
var dto = new LoginRequest { Username = username, Password = password };
var response = await \_client.PostAsync("/login", dto);
return new Result(response.Success);
}
}
6. Infrastructure Layer (платформенный слой)
Содержит реализацию платформенных зависимостей:
-
Геолокация, камера, Bluetooth, уведомления
-
Реализует интерфейсы, объявленные в Domain или Application слое
-
Использует DependencyService или DI через контейнер
Пример:
public class AndroidCameraService : ICameraService
{
public async Task<Stream> CapturePhotoAsync() { /\* ... \*/ }
}
Использование DI-контейнера
Для инверсии зависимостей (например, с ViewModel -> UseCase -> Repository) используется контейнер:
-
Microsoft.Extensions.DependencyInjection
-
Autofac
-
DryIoc
Пример регистрации:
services.AddSingleton<IAuthService, AuthService>();
services.AddTransient<LoginViewModel>();
В Android и iOS проекты внедряется сборка зависимостей из общей библиотеки.
Разделение по проектам
Для удобства сборки и модульности слои выносятся в отдельные сборки:
/MyApp.Solution
├── MyApp.Domain/ # Entities, interfaces
├── MyApp.Application/ # UseCases
├── MyApp.Data/ # Реализация репозиториев, API
├── MyApp.Infrastructure/ # Платформенный код
├── MyApp.Presentation/ # ViewModels
├── MyApp.MobileApp/ # Xamarin.Forms проект
├── MyApp.Android/ # Android UI
├── MyApp.iOS/ # iOS UI
Это позволяет:
-
Пересобирать только изменённые модули
-
Тестировать бизнес-логику без UI
-
Переиспользовать ядро в MAUI, Blazor, Web API
Взаимодействие между слоями
Откуда | Куда | Как |
---|---|---|
ViewModel → UseCase | через DI | Execute() |
--- | --- | --- |
UseCase → Repository | через интерфейс | IAuthRepository |
--- | --- | --- |
Repository → API | через HttpClient или SQLite | |
--- | --- | --- |
View → ViewModel | через биндинги или команды | |
--- | --- | --- |
Platform → Service | через реализацию интерфейсов | |
--- | --- | --- |
Общие принципы
-
SOLID — особенно важны SRP и DIP
-
Интерфейсы между слоями — для гибкости и мокирования
-
Изоляция UI от логики — через MVVM
-
Асинхронность — вся бизнес-логика и репозитории работают через async/await
-
Error handling — через Result<T>, Either, TryCatch, централизованную обработку ошибок
Примеры
Сценарий: Аутентификация
-
ViewModel: LoginViewModel
-
UseCase: LoginUseCase.Execute(string, string)
-
Repository: IAuthRepository.Login(...)
-
Data: AuthApiService.Post(...)
-
DTO: LoginRequest, LoginResponse
-
Infrastructure: HttpClient + JSON-сериализация
Такая организация слоёв позволяет гибко масштабировать приложение, добавлять новые платформы и модули, легко внедрять новые функции и безопасно производить рефакторинг в больших кросс-платформенных проектах.