Що ви опануєте
Ви навчитеся розділяти предметні правила, прикладні сценарії та механізми збереження; описувати рішення через вимоги до якості й перевіряти його. Приклад — сервіс резервування лабораторного обладнання. Архітектура повинна пояснювати, де зберігається правило доступності та як воно забезпечується за роботи кількох клієнтів.
Архітектура та якість
Архітектура описує важливі структурні й поведінкові рішення, відповідальності частин та їх взаємодії. Атрибут якості визначає властивість у контексті: надійність, безпеку, продуктивність або змінюваність. Сценарій якості містить подію, умови середовища, очікувану реакцію й показник. Наприклад, два одночасні запити не повинні створити дві активні резервації одного пристрою.
Назва патерна не є доказом якості. Потрібно показати, як рішення підтримує конкретний сценарій і які витрати створює. Додатковий шар може полегшити заміну сховища й збільшити кількість інтерфейсів. Вибір обґрунтовують очікуваними змінами й ризиками, а не кількістю абстракцій.
Шари відповідальності
Предметна модель містить правила стану. Прикладний сервіс координує сценарій: перевірка користувача, завантаження сутності, виконання операції, збереження. Infrastructure забезпечує конкретні файли, базу чи API. UI приймає подію й показує результат. Така схема допомагає пояснити залежності й перевіряти правила без запуску всього інтерфейсу.
Dependency inversion задає залежність від контракту замість конкретного механізму на важливій межі. Інтерфейс IRepository описує потрібні операції; реалізація працює з конкретним сховищем. Контракт повинен включати гарантії, важливі для сценарію. Save без визначення атомарності може бути недостатнім для одночасних резервацій.
Приклад контракту
using System;
using System.Threading;
using System.Threading.Tasks;
var service = new ReservationService(new DemoStore());
bool accepted = await service.TryReserveAsync(Guid.NewGuid(), CancellationToken.None);
Console.WriteLine(accepted); // True у навчальному store.
public interface IReservationStore
{
Task<bool> TryReserveAsync(Guid equipmentId, CancellationToken token);
}
public sealed class ReservationService(IReservationStore store)
{
public Task<bool> TryReserveAsync(Guid id, CancellationToken token)
{
if (id == Guid.Empty) throw new ArgumentException("Порожній ID");
return store.TryReserveAsync(id, token);
}
}
public sealed class DemoStore : IReservationStore
{
public Task<bool> TryReserveAsync(Guid equipmentId, CancellationToken token)
{
token.ThrowIfCancellationRequested();
return Task.FromResult(true); // Лише stub для показу взаємодії.
}
}
Приклад використовує primary constructor C#12: параметр store доступний методам класу. Task.FromResult створює вже завершений результат без зовнішнього I/O. DemoStore не перевіряє існування чи зайнятість і не придатний як production сховище. Він дозволяє побачити контракт залежності. Повноцінна реалізація має гарантувати узгоджене умовне резервування.
Контракти помилок
Потрібно розділити відмову предметного правила, відсутню сутність, порушення доступу й технічний збій. Bool може бути достатнім для вузького контракту «зайнято чи зарезервовано», але для UI з різними повідомленнями часто потрібний іменований результат. Він повинен містити лише дані, які дозволено показувати користувачу.
Авторизацію виконують до розкриття приватної інформації й зміни стану. Токени не вбудовують у код. Валідація формату Guid не перевіряє права або існування. Умовне збереження та унікальні обмеження в базі можуть забезпечувати потрібний інваріант, але їх перевіряють інтеграційним сценарієм.
Архітектурний запис рішення
ADR — короткий запис рішення: контекст, розглянуті варіанти, вибір і наслідки. Для сховища бронювань контекстом є конкурентні запити й потреба зберігати історію. Варіанти можуть включати умовний update з версією чи унікальне обмеження на активну резервацію. Запис має пояснювати, чому обраний варіант відповідає навантаженню та правилам.
Наслідки включають перевірки, спосіб міграції й залишкові ризики. Якщо змінюється правило часу резервування, ADR переглядають. Історія рішень допомагає новому учаснику відрізнити свідомий компроміс від випадкової структури. Це документація для майбутніх змін, яку потрібно підтримувати актуальною.
Перевірка архітектури
Модульні тести перевіряють правила сутності. Тест сервісу зі stub перевіряє координацію й параметри. Інтеграційний тест сховища підтверджує атомарність. Системна перевірка проходить повідомлення UI. Потрібний рівень визначає місце ризику. Два послідовні виклики in-memory stub не доводять поведінку реальної бази за конкуренції.
Сценарій відмови також важливий: що бачить клієнт, якщо відповідь втрачено після збереження? Ідемпотентність означає, що повторення операції за визначеним ключем не створює зайвого ефекту. Контракт повтору допомагає уникнути подвійних записів, але потребує збереження й перевірки ключа на потрібній межі.
Типові помилки та практика
Помилки: всі правила в UI, інтерфейс із деталями SQL, сховище без контракту конкуренції й загальний catch, який перетворює всі проблеми на успіх. Інша крайність — багато абстракцій без сценарію зміни. Кожна важлива межа повинна мати пояснювану відповідальність і перевірку.
Практичний аналіз
Опишіть втрату мережі після підтвердження бронювання. Як клієнт дізнається про стан? Який ключ дозволить безпечний повтор? Де перевіряти унікальність? Складіть ADR із двома варіантами й поясніть, який інтеграційний тест підтверджує обрану гарантію.
Архітектурне рішення як перевірна гіпотеза
Архітектура визначає значущі межі відповідальності та залежностей. Для резервування домен задає допустимі стани, прикладний сервіс координує сценарій, сховище забезпечує запис, інтерфейс показує результат. Відокремлення полегшує перевірку кожного правила, якщо контракти між частинами достатньо точні.
Метод TryReserve повинен пояснювати, чи перевірка доступності й запис виконуються атомарно. Якщо інтерфейс обіцяє атомарність, реалізація в базі має її забезпечувати транзакцією чи умовним записом. Простий навчальний словник може бути тестовим замінником, але його гарантії конкуренції потрібно назвати. Саме інтерфейс не створює відсутньої гарантії.
ADR — запис архітектурного рішення — містить контекст, варіанти, обрану дію та наслідки. Наприклад, використання окремого сховища полегшує тестування й заміну доступу до даних, але додає контракт та відповідальність за його підтримку. Рішення оцінюють за потребами конкретного процесу, а не лише за назвою шаблону.
Для перевірки ADR задайте сценарій: два клієнти резервують один ресурс, мережа обривається після запису, користувач повторює запит. Визначте потрібні гарантії, місце їх реалізації та спостережуваний результат. Ідентифікатор повторюваної операції може допомогти повернути попередній результат без другого запису. Уточніть, як довго зберігається така відповідність і хто перевіряє права. Ці питання дозволяють зіставити архітектуру з реальними відмовами.
Схема процесу
Визначаємо предметний інваріант.
Самоперевірка
Як обґрунтувати відповідь своїми словами?
Підсумок
Архітектурне рішення пов’язує сценарій якості з відповідальностями й доказами. Контракти сховища, помилок і повторів мають бути явними. ADR зберігає контекст і компроміси для подальших змін.