Презентация 02
Бизнес-логика
BusinessLogicLayer
Сущности, настройки и общие инструменты — и всё это, не подозревая о существовании браузера.
Зачем нужен этот слой?
- Здесь живут сущности (Car, Student…) и их бизнес-правила.
- Он наследует
Recordиз слоя данных — поэтому каждая сущность уже умеет сохраняться и загружаться. - Ему запрещено знать про UI или HTTP: никакого HttpContext, никакого HTML, никакой Session.
- Благодаря этому один и тот же BLL обслуживает сайт, приложение MAUI, ночной Job и тесты.
Бизнес-сущность = небольшой класс
public class Student : Record
{
[Column(Size = 100)] public string FirstName { get; set; }
[Column(Size = 100)] public string LastName { get; set; }
[Column(Size = 20)] public string IdNumber { get; set; }
[Column] public DateTime BirthDate { get; set; }
[Column] public int Grade { get; set; }
[Column] public double Average { get; set; }
[Column] public bool IsActive { get; set; }
[Column(Size = 255)] public string Picture { get; set; }
}
- Поля = столбцы. Ничего сверх этого.
- Бизнес-логика (расчёт среднего балла, проверка удостоверения личности) добавляется методами того же класса.
- Связь с другой сущностью? Свойство её типа — автоматически хранится как
BIGINTс её ID.
Settings — конфигурация, которая живёт в базе данных
// чтение (кеш в памяти, загружается один раз при старте приложения):
string url = Settings.Val["MeshulamUrl"];
// запись:
await Settings.SetAsync("MeshulamUrl", "https://...");
// заполнение значений по умолчанию — каждый проект переопределяет CreateAsync:
public override async Task CreateAsync()
{
await base.CreateAsync();
await AddAsync("MeshulamUrl", "адрес платёжного шлюза", "https://sandbox...");
}
- Почему база данных, а не appsettings.json? Потому что значения можно менять во время работы, для каждого клиента отдельно, без развёртывания.
- Заполнение значений по умолчанию идемпотентно — выполняется при каждом старте и добавляет только недостающие ключи.
Util — общий набор инструментов
- Даты:
DateString,ToDate, еврейские и григорианские месяцы,Decode("еврейский год")=776. - Проверки:
IsValidTz(контрольная цифра),IsPhone,IsValidEmail. - Текст:
StripHTML,CleanHiddenChars,UrlEscape.
- Файлы:
Mime(), проверка расширений,Compress/Decompress(GZip). - Безопасность:
ProtectAppState— шифрование состояния приложения, которое уходит клиенту. - Сеть:
SendHttpRequestAsyncс общим HttpClient.
reuse-first: прежде чем писать новую вспомогательную функцию — убедитесь, что её здесь ещё нет.
Экспорт документов — вместе с ивритом
- Один базовый класс:
Exporter— возвращаетbyte[]; контроллер оборачивает результат вFile(...). - Excel (NPOI) — настоящий RTL-лист; Word (OpenXML) — таблицы с двунаправленным текстом.
- PDF (MigraDoc) — шрифт, встроенный в DLL, и
FlipHebrew, который раскладывает иврит визуально.
Exporter exporter = new ExcelExporter("Students.xlsx", rtl: true);
exporter.AddTable(dataTable);
return File(exporter.GetBytes(), exporter.ContentType, exporter.FileName);
Золотые правила слоя
- Асинхронность до самого конца — метод, который трогает базу данных, возвращает
Task. - Никакой зависимости от UI — всё, чему нужны данные окружения, получает их параметром.
- Сущности остаются лёгкими; общая логика поднимается в
Utilили в базовый класс. Settings— в базу данных, а не в файлы конфигурации.- То, что верно для всех проектов, исправляется сначала здесь, в шаблоне.