Мета та підготовка
Розділити модель даних, контракт сховища й прикладний сценарій, пояснивши асинхронний результат і момент виконання запиту. Потрібно знати List, record, LINQ, Task і CancellationToken. Створіть dotnet new console -n ArchitectureLab. Замініть Program.cs демонстрацією; додаткові пакети не потрібні.
Теоретичний мінімум
Repository надає потрібний контракт доступу до даних. Прикладний сценарій використовує його без прив’язки до механізму. Task представляє результат, доступний після завершення; await спостерігає успіх або помилку. CancellationToken передає запит на скасування. LINQ відбирає активні записи, а ToArray матеріалізує їх для конкретного звіту.
Демонстрація використовує in-memory snapshot із незмінними простими полями. Вона не доводить транзакційність реальної бази й не перевіряє конкурентне збереження. Мета — зробити межі й порядок дій видимими. Для production механізму потрібні окремий контракт узгодженості й інтеграційні перевірки.
Повний демонстраційний код
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
IBookingRepository repository = new MemoryBookingRepository();
using var source = new CancellationTokenSource();
var bookings = await repository.LoadAsync(source.Token);
var report = bookings.Where(b => b.Active)
.OrderBy(b => b.Title, StringComparer.Ordinal)
.Select(b => b.Title).ToArray();
if (report.Length != 2) throw new Exception("Очікується два активні записи");
Console.WriteLine(string.Join(", ", report));
public sealed record Booking(Guid Id, string Title, bool Active);
public interface IBookingRepository
{
Task<IReadOnlyList<Booking>> LoadAsync(CancellationToken token);
}
public sealed class MemoryBookingRepository : IBookingRepository
{
public async Task<IReadOnlyList<Booking>> LoadAsync(CancellationToken token)
{
await Task.Delay(20, token); // Імітація очікування.
return new[] {
new Booking(Guid.NewGuid(), "A", true),
new Booking(Guid.NewGuid(), "B", false),
new Booking(Guid.NewGuid(), "C", true)
};
}
}
Очікується A, C. IReadOnlyList надає контракт читання колекції; він сам по собі не гарантує глибоку незмінність довільних елементів. У цьому прикладі record містить прості значення. StringComparer.Ordinal задає відтворюваний технічний порядок. Delay лише моделює асинхронне очікування й не означає, що дані реально отримано з мережі.
Покрокове виконання
Намалюйте architecture diagram: Program → IBookingRepository → MemoryBookingRepository. Позначте напрям залежності та результат. Запустіть dotnet run, потім змініть дані так, щоб активних було три, й оновіть незалежне очікування відповідно до вашого погодженого прикладу. Додайте порожній набір і поясніть очікуваний порожній звіт.
Перед викликом виконайте source.Cancel() та обробіть OperationCanceledException. Поясніть, чому це окремий результат, а не доказ відсутності даних. Створіть другу реалізацію, яка повертає Task.FromResult без затримки, і переконайтеся, що сценарій працює через той самий інтерфейс. Не додавайте реальні секрети або production базу для демонстрації.
Самостійне завдання
Спроєктуйте сервіс звіту про обладнання з власним repository контрактом. Дані повинні містити категорію, стан і Guid. Реалізуйте два навчальні джерела, запит із двома умовами та явний результат помилки або скасування. Визначте, які дані повертаються незалежними значеннями й коли запит матеріалізується.
Напишіть короткий ADR: контекст, два варіанти межі, вибір і наслідки. Додайте щонайменше чотири перевірки: звичайний звіт, порожній, скасований і помилкове джерело. Основний сервіс, набори та висновок потрібно створити самостійно. Додатковий варіант — параметр моменту часу для відтворюваного відбору майбутніх бронювань.
Типові помилки та налагодження
Виклик .Result замість await може блокувати потрібний контекст. Запит без матеріалізації може повторно читати змінюване джерело. Загальний catch із порожнім списком приховує технічну помилку під виглядом успішного порожнього звіту. Перевірте межу: отримання, фільтрація, матеріалізація, відображення. Кожен етап має власну реакцію на відмову.
Результат і контрольні питання
Подайте проєкт, схему, ADR та результати перевірок. Поясніть, де закінчується контракт repository, хто скасовує операцію й чому in-memory результат не перевіряє database concurrency. Як змінити джерело без зміни правил звіту? Які гарантії потрібні, якщо до сценарію додається збереження?
Перевірка меж сервісу та скасування
Розділіть демонстрацію на три відповідальності: дані Booking, контракт IBookingRepository та реалізація зберігання в пам’яті. Для кожної запишіть, що вона знає про інші. Доменна модель не повинна залежати від способу затримки чи мережевого клієнта, якщо ці деталі не потрібні її правилам.
Перевірте результат читання при порожньому сховищі та кількох станах записів. Active-фільтр повинен відповідати вашому визначенню активності. Збережіть id, які очікуються в результаті, і фактичну послідовність. Якщо порядок важливий, задайте його явно, а не покладайтеся на випадковий порядок вставлення.
Передайте вже скасований CancellationToken у навчальну асинхронну операцію та зафіксуйте реакцію. Task.Delay із токеном може завершитися скасуванням; це відрізняється за змістом від помилки предметного правила. Обробка повинна зберігати цю інформацію для викликача. Не перетворюйте всі винятки на порожній успішний список.
В ADR власного рішення зазначте, які гарантії дає сховище в пам’яті й що потрібно для виробничої бази. Перевірка двох клієнтів, унікальність та атомарний запис можуть вимагати додаткової реалізації. Основне завдання — описати контракти й побудувати мінімальну перевірну систему за ними. Подайте схему залежностей, фактичний запуск та один сценарій відмови з поясненням стану після неї.
Окремий контрольний сценарій
Для тестового repository запишіть політику копіювання результатів. Повернення нового List відділяє структуру списку, але його елементи можуть лишатися спільними. Якщо клієнт може змінювати Booking, потрібно визначити, чи це змінить сховище. Використання незмінних record-даних або явного перетворення допомагає погодити межу. Перевірте власну політику одним прикладом та зафіксуйте результат, щоб контракт читання залишався зрозумілим після заміни реалізації.
Схема процесу
Задаємо асинхронний доступ і cancellation.
Самоперевірка
Як обґрунтувати відповідь своїми словами?
Підсумок
Архітектурна межа відокремлює джерело від правил звіту. Async контракт і cancellation мають визначену поведінку. ADR та виконувані сценарії пояснюють гарантії й межі навчальної реалізації.