Маленька архітектура


Я хочу стати архітектором З:
Це хороша мета для розробника
Я хочу керувати командою і приймати важливі рішення про бази даних, фреймворках і веб-сервісах і все таке.
Хм. Ну, тоді ти зовсім не хочеш стати архітектором.
Звичайно хочу! Я хочу бути тією людиною, яка приймає всі важливі рішення.
Це добре, але ти не перерахував важливих рішень. Ти перерахував рішення, що не грають особливої ролі.
У сенсі? База даних – це не важливе рішення? Знаєш, скільки грошей ми витрачаємо на них?
Швидше всього дуже багато. Та ні, база даних – це одне з найважливіших рішень.
Як можна таке говорити? База даних знаходиться в самому центрі системи! Там зібрані всі дані, вони сортуються, індексуються і до них здійснюється доступ. Без неї не буде!
База даних це просто пристрій вводу-виводу. Так вийшло, що вона надає деякі корисні інструменти для сортування, запитів і звітів, але все це – допоміжні аспекти в рамках системної архітектури.
Допоміжні? Це божевілля.
Так, допоміжні. Бізнес-правила твоєї системи, можливо, використовують деякі з цих інструментів. Але вони не істотні для бізнес-правил. Якщо потрібно, то можна замінити ці інструменти іншими інструментами, але бізнес-правила залишаться тими ж.
Ну, так, але доведеться переписувати весь код, тому що він використовує інструменти оригінальної бази даних.
Ну, це твоя проблема.
Що ти маєш на увазі?
Твоя проблема в тому, що ти віриш, що бізнес-правила залежать від інструментів бази даних. Це не так. У всякому разі, вони не повинні залежати якщо ти розробив гарну архітектуру.
Це безумство. Як я можу створювати бізнес-правила, які не використовують інструменти, які потрібно використовувати?
Я не сказав, що вони не використовують інструменти бази даних. Я сказав, що вони не повинні залежати від них. Бізнес-правила не повинні знати яку конкретну базу даних ти використовуєш.
Як змусити бізнес-правила використовувати інструменти без знання, що це за інструменти?
Потрібно інвертувати залежність. У тебе база даних залежить від бізнес-правил. Зроби так, щоб бізнес-правила не залежали від бази.
Ти говориш якусь нісенітницю.
Навпаки. Я говорю мовою архітектури. Це принцип інверсії залежностей. Галузеві аспекти повинні залежати від високорівневих аспектів.
Більше белиберды! Високорівневі аспекти (я так розумію, ти говориш про бізнес-правила) викликають низькорівневі (я так розумію, ти маєш на увазі базу даних). Так що високорівневі модулі залежать від низькорівневих так само, як викликають залежать від викликаються. Всі це знають!
рантайме це правда. Але у момент компіляції нам потрібно інвертувати залежність. Вихідний код високого рівня не повинен згадувати вихідний код низького рівня.
Ну блін! Не можна викликати щось, не згадуючи його.
Звичайно можна. В цьому суть об'єктної орієнтації.
Суть об'єктної орієнтації в моделюванні реального світу, в комбінації даних і функцій в зв'язані об'єкти. В організації коду в інтуїтивну архітектуру.
Так тебе навчили?
Все це знають. Очевидно, що це правда.
Не сумніваюся. Не сумніваюся. І все таки, використовуючи принципи об'єктної орієнтації можна насправді викликати щось не згадуючи цього.
OK. Як?
Ти знаєш, що в об'єктно-орієнтованому дизайні об'єкти посилають повідомлення один одному?
Так. Звичайно.
А ти знаєш, що відправник повідомлення не знає тип одержувача?
Це залежить від мови. в Java відправник знає як мінімум базовий тип одержувача. В Ruby відправник знає як мінімум те, що одержувач може обробити надіслане повідомлення.
Так. Але ні в одному, ні в іншому випадку відправник не знає конкретного типу одержувача.
Ага. ОК. Так.
Отже, відправник може бути причиною запуску функції в одержувача, при цьому не згадуючи тип одержувача.
Ага. Правильно. Це я розумію. Але відправник все ще залежить від одержувача.
рантайме, так. Але не при компіляції. Вихідний код відправника не згадує і не залежить від вихідного коду отримувача. Вихідний код одержувача залежить від вихідного коду відправника.
Нееее. Відправник все ще залежить від класу, якому надсилається повідомлення.
Можливо, нам потрібні приклади. Я буду писати на Java. Спочатку пакет відправника:
package sender;

public class Sender {
private Receiver receiver;

public Sender(Receiver r) {
receiver = r;
}

public void doSomething() {
receiver.receiveThis();
}

public interface Receiver {
void receiveThis();
}
}

Тепер пакет одержувача.
receiver package;

import sender.Sender;

public class SpecificReceiver implements Sender.Receiver {
public void receiveThis() {
//do something interesting.
}
}

Зауваж, що одержувач залежить від відправника. Також зверни увагу, що SpecificReceiver залежить від Sender. І нічого у відправника не знає про одержувача.
Так, але це чітерство. Ти вставив інтерфейс одержувача в клас відправника.
Тепер на починаєш розуміти.
Розуміти що?
Принципи архітектури, звичайно ж. Відправники володіють інтерфейсами, які одержувачі повинні реалізовувати.
Якщо це означає, що доведеться використовувати вкладені класи, то...
Вкладені класи це всього лише один із способів досягти мети. Є інші.
ДОБРЕ, постривай. Причому тут бази даних? Ми почали з цього.
Давай глянемо на інший код. Ось перше бізнес-правило:
package businessRules;

import entities.Something;

public class BusinessRule {
private BusinessRuleGateway gateway;

public BusinessRule(BusinessRuleGateway gateway) {
this.gateway = gateway;
}

public void execute(String id) {
gateway.startTransaction();
Something thing = gateway.getSomething(id);
thing.makeChanges();
gateway.saveSomething(thing);
gateway.endTransaction();
}
}

Це бізнес-правило особливо нічого не робить.
Це просто приклад. У тебе швидше за все багато таких класів, що реалізують різні правила.
ДОБРЕ, що це за
Gateway
там?
Він надає всі методу доступу до даних, які використовує бізнес-правило. Ось його реалізація:
package businessRules;

import entities.Something;

public interface BusinessRuleGateway {
Something getSomething(String id);
void startTransaction();
void saveSomething(Something thing);
void endTransaction();
}

Зауваж, що це не пакет businessRules.
Ага, ОК. А що за клас
Something
?
Він представляє простий бізнес-об'єкт. Я поклав його в пакет під назвою operation.
package entities;

public class Something {
public void makeChanges() {
//...
}
}

І, нарешті, ось реалізація BusinessRuleGateway. Цей клас знає про саму базі даних:
package database;

import businessRules.BusinessRuleGateway;
import entities.Something;

public class MySqlBusinessRuleGateway implements BusinessRuleGateway {
public Something getSomething(String id) {
// use MySQL to get a thing.
}

public void startTransaction() {
// start MySQL transaction
}

public void saveSomething(Something thing) {
// save thing in MySQL
}

public void endTransaction() {
// end MySql transaction
}
}

Знову ж, зауваж, що бізнес-правила викликають базу даних під час виконання, але під час компіляції це робить пакет database. Він згадує і залежить від пакету thebusinessRules.
Ок, ок, здається, я зрозумів. Ти просто використовуєш поліморфізм щоб приховати реалізацію бази даних бізнес-правил. Але все ще потрібен інтерфейс, який надає бізнес-правилами доступ до всіх інструментів бази даних.
ні, зовсім Ні. Ми не намагаємося надати бізнес-правилам всі інструменти бази даних. Навпаки, наші бізнес-правила створюють інтерфейси тільки для того, що їм потрібно. Реалізація цих інтерфейсів вже може викликати відповідні інструменти.
Ага, але якщо бізнес-правилами потрібні всі ці інструменти, то доведеться покласти їх в інтерфейс
gateway
.
Ах. Бачу, що ти ще не розумієш.
Не розумію чого? Мені здається, все гранично ясно.
Кожен бізнес-правило визначає інтерфейс лише для необхідного йому способу доступу до даних.
Стоп. Що?
Це називається принцип поділу інтерфейсу. Кожен клас бізнес-правила буде використовувати тільки частина механізмів бази даних. Тому кожне бізнес-правило надає інтерфейс, що дає йому доступ до цих механізмів.
Але це означає, що доведеться мати справу з купою інтерфейсів, і купою маленьких класів реалізації для виклику інших класів бази даних.
But that means that you're going to have lots of interfaces, and lots of little implementation classes call that other database classes.
Добре. Бачу, що тепер ти починаєш розуміти.
Але це бардак і трата часу! Навіщо таке робити?
Щоб тримати все в чистоті і економити час.
Ой, все! Це просто купа коду заради коду.
Навпаки, ці важливі архітектурні рішення дозволять відкладати нерелевантні рішення.
Що ти маєш на увазі?
Пам'ятаєш, ти почав з того, що хотів стати архітектором? Ти хотів приймати всі найважливіші рішення?
Так, цього я й хочу.
Серед цих рішень були база даних, веб-сервер і фреймворки.
Ага, і ти сказав, що це не важливі рішення. Ти сказав, що вони не грають особливої ролі.
Вірно. Не грають. Важливі рішення, які приймає архітектор, дозволяють НЕ приймати рішення з приводу бази даних, веб-сервера і фреймворків.
Але потрібно прийняти ці рішення спочатку!
Ні, не потрібно. Насправді, потрібно зробити так, щоб була можливість приймати ці рішення набагато пізніше в циклі розробки – коли буде більше інформації.
Горе – це архітектор, який передчасно вибирає базу даних, а потім розуміє, що плоскої файлової структури було б достатньо.
Горе – це архітектор, який передчасно вибирає веб-сервер, а потім розуміє, що команді потрібен був простий socket-інтерфейс.
Горе – це команда, чий архітектор передчасно нав'язує на неї фреймворк, а потім розуміє, що фреймворк надає не потрібні їм можливості і додає обмеження, з якими неможливо жити.
Благословенна команда, чиї архітектори надали спосіб відкладання всіх цих рішень до моменту, коли у команди буде достатньо інформації для їх прийняття.
Благословенна команда, чиї архітектори так добре ізолювали їх від ресурсномістких пристроїв введення-виведення і фреймворків, що команда здатна створювати швидкі і легкі середовища тестування.
Благословенна команда, чиї архітектори хвилюються про по-справжньому важливі речі, і відкладають не важливі.
Дурниця. Я нічого не розумію.
Ну, можливо, зрозумієш років через десять… Якщо не перейдеш в менеджери.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.