Enterprise ландшафт и капабилитита
Накратко: Картата на капабилититата описва какво прави бизнесът, независимо от системите; свързана с приложенията, тя показва дупките и дублирането, а моделът TIME казва кое да развиваш.
Проблем
Три системи за фактури и нито една за връщания
Магазинът расте пет години. Магазинът сам издава фактура при поръчка. Счетоводството си купува своя система и издава фактури за фирмите. После маркетингът плаща SaaS за фактури на абонаментите, защото „беше по-бързо“.
Финансовият директор пита защо фактурите имат три номерации. В същото време отделът за клиенти води връщанията в имейли и таблица, а счетоводителката преписва всяко връщане на ръка.
Всеки екип познава своята система. Никой не вижда цялото.
Идеята
Какво прави бизнесът, не кои системи има
Enterprise архитектурата гледа цялата фирма: всички системи, как се свързват и за какво служат. Започва не от системите, а от капабилититата: какво прави бизнесът. Продава, фактурира, доставя, приема връщания. Това се мени рядко. Системите, отделите и доставчиците се сменят, а магазинът фактурира и след десет години.
Картата на капабилититата ги подрежда по групи, обикновено на една страница:
static final List<Group> MAP = List.of( new Group("Продажби", List.of("Каталог", "Количка", "Поръчки")), new Group("Финанси", List.of("Плащания", "Фактуриране", "Счетоводство")), new Group("Логистика", List.of("Склад", "Доставка", "Връщания")), new Group("Клиенти", List.of("Известия", "Поддръжка")));
static final List<App> APPS = List.of( new App("Магазин", List.of("Каталог", "Количка", "Поръчки", "Фактуриране")), new App("Склад 2009", List.of("Склад")), new App("Счетоводна система", List.of("Счетоводство", "Фактуриране")), new App("SaaS за фактури", List.of("Фактуриране")), new App("Платежен доставчик", List.of("Плащания")), new App("Куриер", List.of("Доставка")), new App("Имейл услуга", List.of("Известия")), new App("Helpdesk", List.of("Поддръжка")));record Group(String name, List<String> capabilities) {}
record App(String name, List<String> capabilities) {}Под картата се закачат приложенията: за всяко капабилити кои системи го поддържат. Тогава се виждат две неща, които иначе не се виждат:
- дупка: капабилити без система; хората го вършат на ръка;
- дублиране: капабилити с няколко системи; плащаш няколко пъти и имаш няколко версии на истината.
Виж го
Картата, оцветена по покритие
// кои системи поддържат капабилититоstatic List<String> appsFor(String cap, List<App> apps) { List<String> names = new ArrayList<>(); for (App app : apps) if (app.capabilities().contains(cap)) names.add(app.name()); return names;}
static String coverage(List<String> names) { if (names.isEmpty()) return "✗ няма система"; if (names.size() > 1) return "⚠ " + names.size() + " системи: " + String.join(", ", names); return names.get(0);}Стъпка по стъпка, с думи
- Картата на капабилититата: какво прави бизнесът, по групи. Още без нито една система.
- 8-те приложения идват едно по едно. Всяко казва кои капабилитита поддържа и броят им расте.
- Капабилити с точно една система е покрито: зелено.
- Фактуриране: правят го 3 системи. Три лиценза, три номерации на фактурите, три места за поправка.
- Връщания: нито една система. Хората ги водят в имейли и таблици.
- Картата, оцветена по покритие: 1 дупка и 1 дублиране. Оттук тръгва целевото състояние.
// кои системи поддържат капабилититоstatic List<String> appsFor(String cap, List<App> apps) { List<String> names = new ArrayList<>(); for (App app : apps) if (app.capabilities().contains(cap)) names.add(app.name()); return names;}
static String coverage(List<String> names) { if (names.isEmpty()) return "✗ няма система"; if (names.size() > 1) return "⚠ " + names.size() + " системи: " + String.join(", ", names); return names.get(0);}
static void printMap(List<Group> map, List<App> apps) { int gaps = 0, duplicated = 0; for (Group group : map) { System.out.println(group.name()); for (String capability : group.capabilities()) { List<String> names = appsFor(capability, apps); if (names.isEmpty()) gaps++; if (names.size() > 1) duplicated++; System.out.println(" " + capability + ": " + coverage(names)); } } System.out.println("Дупки: " + gaps + ", дублирани: " + duplicated);}printMap(MAP, APPS);Продажби Каталог: Магазин Количка: Магазин Поръчки: МагазинФинанси Плащания: Платежен доставчик Фактуриране: ⚠ 3 системи: Магазин, Счетоводна система, SaaS за фактури Счетоводство: Счетоводна системаЛогистика Склад: Склад 2009 Доставка: Куриер Връщания: ✗ няма системаКлиенти Известия: Имейл услуга Поддръжка: HelpdeskДупки: 1, дублирани: 1List.of пази реда, а record дава конструктор и методи за полетата без нито ред повече.Програмата е малка, но истинската работа е в данните: кое е отделно капабилити и коя система какво наистина прави. Това се събира с разговори с екипите, не с код.
Пробвай
Попълни
Как проверяваш, че приложението поддържа капабилитито?
for (App app : apps)
if (app.capabilities().(cap))
names.add(app.name());Проверката е дали капабилитито е в списъка на приложението. Списъкът е малък, затова линейното търсене е напълно достатъчно.
Идеята
Портфолио на приложенията: TIME
Картата казва къде е проблемът. Портфолиото на приложенията казва какво да правиш с всяка система. За всяка записваш колко струва на година и две оценки от 1 до 5:
- бизнес стойност: колко трябва на бизнеса;
- техническо състояние: колко е здрава отвътре (поддържа ли се, има ли хора, които я знаят, колко често пада).
Двете оценки дават четири решения, известни като модела TIME (Gartner):
| техниката е здрава | техниката е болна | |
|---|---|---|
| ценно за бизнеса | Invest: инвестирай | Migrate: мигрирай |
| малко ценно | Tolerate: търпи | Eliminate: махни |
record Rating(String app, int costPerYear, int business, int technical) {}// TIME: ценно ли е за бизнеса и здраво ли е отвътреstatic String decide(Rating r) { boolean valuable = r.business() >= 3; boolean healthy = r.technical() >= 3; if (valuable && healthy) return "инвестирай"; if (valuable) return "мигрирай"; if (healthy) return "търпи"; return "махни";}Цените по-долу са примерни, а оценките са на екипа.
List<Rating> ratings = List.of( new Rating("Магазин", 60000, 5, 3), new Rating("Склад 2009", 18000, 4, 1), new Rating("Счетоводна система", 24000, 4, 4), new Rating("SaaS за фактури", 9600, 2, 2), new Rating("Helpdesk", 4800, 2, 4));int total = 0, saved = 0;for (Rating r : ratings) { String decision = decide(r); System.out.println(r.app() + ": " + decision + " (бизнес " + r.business() + ", техника " + r.technical() + ")"); total += r.costPerYear(); if (decision.equals("махни")) saved += r.costPerYear();}System.out.println("Годишно: " + total + " €, махаш " + saved + " €");Магазин: инвестирай (бизнес 5, техника 3)Склад 2009: мигрирай (бизнес 4, техника 1)Счетоводна система: инвестирай (бизнес 4, техника 4)SaaS за фактури: махни (бизнес 2, техника 2)Helpdesk: търпи (бизнес 2, техника 4)Годишно: 116400 €, махаш 9600 €TIME не е присъда, а начало на разговор. „Търпи“ значи „не харчи за нея“, не „забрави я“: Helpdesk работи и не пречи. „Махни“ иска дата и план, иначе приложението живее още пет години.
Пробвай
Предвиди изхода
Мобилното приложение носи половината поръчки (бизнес 4), но е писано на рамка, която вече не се поддържа (техника 2). Какво казва decide?
System.out.println(decide(new Rating("Мобилно приложение", 30000, 4, 2)));Ценно (4 ≥ 3), но болно (2 < 3): мигрирай. Функцията трябва на бизнеса, системата не.
Пробвай
Намери грешката
Колега е написал decide с копиране и поставяне. Сега портфолиото казва:
Магазин: инвестирай (бизнес 5, техника 3)Склад 2009: инвестирай (бизнес 4, техника 1)Счетоводна система: инвестирай (бизнес 4, техника 4)SaaS за фактури: махни (бизнес 2, техника 2)Helpdesk: махни (бизнес 2, техника 4)Годишно: 116400 €, махаш 14400 €Склад 2009 изведнъж е за инвестиция, а Helpdesk за махане. На кой ред е грешката?
Избери реда с грешката.
healthy чете бизнес оценката вместо техническата: редът е копиран от горния и не е сменен. Така Склад 2009 (бизнес 4) изглежда здрав, а Helpdesk (бизнес 2) болен. Две оценки, които изглеждат еднакво, лесно се объркват: тест с приложение, при което двете оценки са различни, хваща грешката веднага.
Идеята
Целево състояние и пътна карта
Картата и портфолиото дават целевото състояние: как трябва да изглежда ландшафтът след година или две. За магазина:
- фактурирането е само в счетоводната система, с една номерация; SaaS-ът за фактури излиза (махни), а модулът за фактури в магазина се изключва;
- складът се подменя (мигрирай) с нов, който поддържа и връщанията: дупката се запълва там, където са стоките;
- всичко останало остава.
Пътната карта е редът на стъпките до там, с междинни състояния, които работят:
- новият склад поема връщанията, а старият остава за стоките; връзката към Склад 2009 минава през anti-corruption layer, за да не изтича CSV-то му в новия код;
- стоките минават към новия склад постепенно, както в „Смяна на жива система“, докато Склад 2009 не остане без трафик;
- фактурите на магазина минават към счетоводната система; номерацията се сменя от нова година, за да е ясно за счетоводителите;
- абонаментът за SaaS-а се прекратява след последната фактура в него.
Всяка стъпка е отделно решение с ADR, своя цена и свои рискове в регистъра. Редът се избира по болката и по зависимостите: връщанията са първи, защото днес се губят пари.
По-точно, за любопитните
Рамките. TOGAF е най-известната рамка за enterprise архитектура: описва метод (ADM) за тези стъпки и много документи. ArchiMate е език за диаграми на ландшафта, а Zachman е таблица за класифициране на описанията. Тук само ги назоваваме: курсът учи работата, а сертификацията е отделен път.
Капабилити, процес и отдел. Капабилити е „какво“ (фактуриране). Процесът е „как“, по стъпки (поръчка → фактура → плащане). Отделът е „кой“. Картата е от капабилитита, защото те се менят най-рядко.
Инструментите. Големите фирми държат портфолиото в специални EA инструменти или в CMDB. Малката фирма го държи в таблица. Важното е да е едно място и да се обновява.
Идеята
Кога помага и кога е бюрокрация
Enterprise архитектурата помага, когато:
- системите са десетки и никой не ги държи наум;
- фирмата се слива с друга и трябва да реши коя система остава;
- харчът за софтуер расте и никой не знае за какво се плаща;
- регулатор пита къде са данните на клиентите.
Вреди, когато:
- картата е цел. Начертана, разпечатана и забравена. Карта, по която не е взето нито едно решение, е украса.
- борд одобрява всяка библиотека. Решенията, които един екип може да смени сам, остават на екипа. Централно се решава само това, което струва скъпо за цялата фирма: обща идентичност, плащания, данни за клиенти.
- фирмата е малка. Три системи и един екип нямат нужда от рамка: стига таблица на една страница и ADR-и.
- рамката заменя разговора. Документите по рамката не са работата. Работата е решението, което идва от тях.
Упражнение
Целевото състояние
Реши задачата в редактора си.
Запиши целевото състояние като нов списък с приложения: магазинът вече не фактурира, SaaS-ът за фактури го няма, а Склад 2009 е заменен от „Нов склад“, който поддържа склада и връщанията. Пусни същата проверка на покритието върху него:
Продажби Каталог: Магазин Количка: Магазин Поръчки: МагазинФинанси Плащания: Платежен доставчик Фактуриране: Счетоводна система Счетоводство: Счетоводна системаЛогистика Склад: Нов склад Доставка: Куриер Връщания: Нов складКлиенти Известия: Имейл услуга Поддръжка: HelpdeskДупки: 0, дублирани: 0Още задачи
- Fitness function за ландшафта. Направи проверката тест, който пада, ако има дупка или дублиране без записано решение (списък с разрешени дублирания и причината им).
- Цената на дублирането. Добави към всяко приложение цената му и отпечатай колко струват на година всички системи на дублираните капабилитита.
- Две държави. Магазинът тръгва в Гърция с местен куриер. Две системи за доставка нарушение ли са? Как ще го покажеш на картата?
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: Нефункционални изисквания, които се мерят · Интеграция със стари и чужди системи · Смяна на жива система (strangler fig, expand–contract) · Архитектурен преглед
- Продължава в: Твоята първа архитектурна задача
- Задачи по темата: Архитектът в работа