Към съдържанието
wannacode

Enterprise ландшафт и капабилитита

Накратко: Картата на капабилититата описва какво прави бизнесът, независимо от системите; свързана с приложенията, тя показва дупките и дублирането, а моделът TIME казва кое да развиваш.

Проблем

Три системи за фактури и нито една за връщания

Магазинът расте пет години. Магазинът сам издава фактура при поръчка. Счетоводството си купува своя система и издава фактури за фирмите. После маркетингът плаща SaaS за фактури на абонаментите, защото „беше по-бързо“.

Финансовият директор пита защо фактурите имат три номерации. В същото време отделът за клиенти води връщанията в имейли и таблица, а счетоводителката преписва всяко връщане на ръка.

Всеки екип познава своята система. Никой не вижда цялото.

Идеята

Какво прави бизнесът, не кои системи има

Enterprise архитектурата гледа цялата фирма: всички системи, как се свързват и за какво служат. Започва не от системите, а от капабилититата: какво прави бизнесът. Продава, фактурира, доставя, приема връщания. Това се мени рядко. Системите, отделите и доставчиците се сменят, а магазинът фактурира и след десет години.

Картата на капабилититата ги подрежда по групи, обикновено на една страница:

static readonly Group[] Map =
[
new("Продажби", ["Каталог", "Количка", "Поръчки"]),
new("Финанси", ["Плащания", "Фактуриране", "Счетоводство"]),
new("Логистика", ["Склад", "Доставка", "Връщания"]),
new("Клиенти", ["Известия", "Поддръжка"]),
];
static readonly App[] Apps =
[
new("Магазин", ["Каталог", "Количка", "Поръчки", "Фактуриране"]),
new("Склад 2009", ["Склад"]),
new("Счетоводна система", ["Счетоводство", "Фактуриране"]),
new("SaaS за фактури", ["Фактуриране"]),
new("Платежен доставчик", ["Плащания"]),
new("Куриер", ["Доставка"]),
new("Имейл услуга", ["Известия"]),
new("Helpdesk", ["Поддръжка"]),
];
record Group(string Name, string[] Capabilities);
record App(string Name, string[] Capabilities);

Под картата се закачат приложенията: за всяко капабилити кои системи го поддържат. Тогава се виждат две неща, които иначе не се виждат:

  • дупка: капабилити без система; хората го вършат на ръка;
  • дублиране: капабилити с няколко системи; плащаш няколко пъти и имаш няколко версии на истината.

Виж го

Картата, оцветена по покритие

// кои системи поддържат капабилитито
static List<string> AppsFor(string cap,
IEnumerable<App> apps) =>
apps.Where(a => a.Capabilities.Contains(cap))
.Select(a => a.Name)
.ToList();
static string Coverage(List<string> names)
{
if (names.Count == 0)
return "✗ няма система";
if (names.Count > 1)
return $"⚠ {names.Count} системи: "
+ string.Join(", ", names);
return names[0];
}
Стъпка по стъпка, с думи
  1. Картата на капабилититата: какво прави бизнесът, по групи. Още без нито една система.
  2. 8-те приложения идват едно по едно. Всяко казва кои капабилитита поддържа и броят им расте.
  3. Капабилити с точно една система е покрито: зелено.
  4. Фактуриране: правят го 3 системи. Три лиценза, три номерации на фактурите, три места за поправка.
  5. Връщания: нито една система. Хората ги водят в имейли и таблици.
  6. Картата, оцветена по покритие: 1 дупка и 1 дублиране. Оттук тръгва целевото състояние.
// кои системи поддържат капабилитито
static List<string> AppsFor(string cap,
IEnumerable<App> apps) =>
apps.Where(a => a.Capabilities.Contains(cap))
.Select(a => a.Name)
.ToList();
static string Coverage(List<string> names)
{
if (names.Count == 0)
return "✗ няма система";
if (names.Count > 1)
return $"⚠ {names.Count} системи: "
+ string.Join(", ", names);
return names[0];
}
static void PrintMap(IEnumerable<Group> map, IEnumerable<App> apps)
{
int gaps = 0, duplicated = 0;
foreach (var group in map)
{
Console.WriteLine(group.Name);
foreach (var capability in group.Capabilities)
{
var names = AppsFor(capability, apps);
if (names.Count == 0) gaps++;
if (names.Count > 1) duplicated++;
Console.WriteLine($" {capability}: {Coverage(names)}");
}
}
Console.WriteLine($"Дупки: {gaps}, дублирани: {duplicated}");
}
PrintMap(Map, Apps);
Изход
Продажби
Каталог: Магазин
Количка: Магазин
Поръчки: Магазин
Финанси
Плащания: Платежен доставчик
Фактуриране: ⚠ 3 системи: Магазин, Счетоводна система, SaaS за фактури
Счетоводство: Счетоводна система
Логистика
Склад: Склад 2009
Доставка: Куриер
Връщания: ✗ няма система
Клиенти
Известия: Имейл услуга
Поддръжка: Helpdesk
Дупки: 1, дублирани: 1
Картата и приложенията са масиви от record. Ползваме масиви, а не Dictionary: редът на Dictionary на практика е редът на добавяне, но не е обещан.

Програмата е малка, но истинската работа е в данните: кое е отделно капабилити и коя система какво наистина прави. Това се събира с разговори с екипите, не с код.

Пробвай

Попълни

Как проверяваш, че приложението поддържа капабилитито?

apps.Where(a => a.Capabilities.(cap))

Идеята

Портфолио на приложенията: 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)
{
bool valuable = r.Business >= 3;
bool healthy = r.Technical >= 3;
if (valuable && healthy) return "инвестирай";
if (valuable) return "мигрирай";
if (healthy) return "търпи";
return "махни";
}

Цените по-долу са примерни, а оценките са на екипа.

List<Rating> ratings =
[
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;
foreach (var r in ratings)
{
var decision = Decide(r);
Console.WriteLine($"{r.App}: {decision} (бизнес {r.Business}, техника {r.Technical})");
total += r.CostPerYear;
if (decision == "махни") saved += r.CostPerYear;
}
Console.WriteLine($"Годишно: {total} €, махаш {saved} €");
Изход
Магазин: инвестирай (бизнес 5, техника 3)
Склад 2009: мигрирай (бизнес 4, техника 1)
Счетоводна система: инвестирай (бизнес 4, техника 4)
SaaS за фактури: махни (бизнес 2, техника 2)
Helpdesk: търпи (бизнес 2, техника 4)
Годишно: 116400 €, махаш 9600 €

TIME не е присъда, а начало на разговор. „Търпи“ значи „не харчи за нея“, не „забрави я“: Helpdesk работи и не пречи. „Махни“ иска дата и план, иначе приложението живее още пет години.

Пробвай

Предвиди изхода

Мобилното приложение носи половината поръчки (бизнес 4), но е писано на рамка, която вече не се поддържа (техника 2). Какво казва decide?

Console.WriteLine(Decide(new Rating("Мобилно приложение", 30000, 4, 2)));
Отговор

Пробвай

Намери грешката

Колега е написал decide с копиране и поставяне. Сега портфолиото казва:

Изход
Магазин: инвестирай (бизнес 5, техника 3)
Склад 2009: инвестирай (бизнес 4, техника 1)
Счетоводна система: инвестирай (бизнес 4, техника 4)
SaaS за фактури: махни (бизнес 2, техника 2)
Helpdesk: махни (бизнес 2, техника 4)
Годишно: 116400 €, махаш 14400 €

Склад 2009 изведнъж е за инвестиция, а Helpdesk за махане. На кой ред е грешката?

Избери реда с грешката.

Идеята

Целево състояние и пътна карта

Картата и портфолиото дават целевото състояние: как трябва да изглежда ландшафтът след година или две. За магазина:

  • фактурирането е само в счетоводната система, с една номерация; SaaS-ът за фактури излиза (махни), а модулът за фактури в магазина се изключва;
  • складът се подменя (мигрирай) с нов, който поддържа и връщанията: дупката се запълва там, където са стоките;
  • всичко останало остава.

Пътната карта е редът на стъпките до там, с междинни състояния, които работят:

  1. новият склад поема връщанията, а старият остава за стоките; връзката към Склад 2009 минава през anti-corruption layer, за да не изтича CSV-то му в новия код;
  2. стоките минават към новия склад постепенно, както в „Смяна на жива система“, докато Склад 2009 не остане без трафик;
  3. фактурите на магазина минават към счетоводната система; номерацията се сменя от нова година, за да е ясно за счетоводителите;
  4. абонаментът за SaaS-а се прекратява след последната фактура в него.

Всяка стъпка е отделно решение с ADR, своя цена и свои рискове в регистъра. Редът се избира по болката и по зависимостите: връщанията са първи, защото днес се губят пари.

По-точно, за любопитните

Рамките. TOGAF е най-известната рамка за enterprise архитектура: описва метод (ADM) за тези стъпки и много документи. ArchiMate е език за диаграми на ландшафта, а Zachman е таблица за класифициране на описанията. Тук само ги назоваваме: курсът учи работата, а сертификацията е отделен път.

Капабилити, процес и отдел. Капабилити е „какво“ (фактуриране). Процесът е „как“, по стъпки (поръчка → фактура → плащане). Отделът е „кой“. Картата е от капабилитита, защото те се менят най-рядко.

Инструментите. Големите фирми държат портфолиото в специални EA инструменти или в CMDB. Малката фирма го държи в таблица. Важното е да е едно място и да се обновява.

Идеята

Кога помага и кога е бюрокрация

Enterprise архитектурата помага, когато:

  • системите са десетки и никой не ги държи наум;
  • фирмата се слива с друга и трябва да реши коя система остава;
  • харчът за софтуер расте и никой не знае за какво се плаща;
  • регулатор пита къде са данните на клиентите.

Вреди, когато:

  • картата е цел. Начертана, разпечатана и забравена. Карта, по която не е взето нито едно решение, е украса.
  • борд одобрява всяка библиотека. Решенията, които един екип може да смени сам, остават на екипа. Централно се решава само това, което струва скъпо за цялата фирма: обща идентичност, плащания, данни за клиенти.
  • фирмата е малка. Три системи и един екип нямат нужда от рамка: стига таблица на една страница и ADR-и.
  • рамката заменя разговора. Документите по рамката не са работата. Работата е решението, което идва от тях.

Упражнение

Целевото състояние

Реши задачата в редактора си.

Запиши целевото състояние като нов списък с приложения: магазинът вече не фактурира, SaaS-ът за фактури го няма, а Склад 2009 е заменен от „Нов склад“, който поддържа склада и връщанията. Пусни същата проверка на покритието върху него:

Изход
Продажби
Каталог: Магазин
Количка: Магазин
Поръчки: Магазин
Финанси
Плащания: Платежен доставчик
Фактуриране: Счетоводна система
Счетоводство: Счетоводна система
Логистика
Склад: Нов склад
Доставка: Куриер
Връщания: Нов склад
Клиенти
Известия: Имейл услуга
Поддръжка: Helpdesk
Дупки: 0, дублирани: 0

Още задачи

  • Fitness function за ландшафта. Направи проверката тест, който пада, ако има дупка или дублиране без записано решение (списък с разрешени дублирания и причината им).
  • Цената на дублирането. Добави към всяко приложение цената му и отпечатай колко струват на година всички системи на дублираните капабилитита.
  • Две държави. Магазинът тръгва в Гърция с местен куриер. Две системи за доставка нарушение ли са? Как ще го покажеш на картата?

Тест

Провери се

1. Какво е капабилити?
2. Капабилити, което поддържат три системи, е…
3. Склад 2009 е важен за бизнеса (4), но е в лошо техническо състояние (1). Какво казва TIME?
4. Кога картата на капабилититата е бюрокрация?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·