Розділ 9. Обід на Хрещатику
Обід на Хрещатику
Богдана я знайшов через статті. Він вів технічний канал у Telegram про архітектуру BAS — без політики, без флеймів, без хайпу: розбори типових кейсів, пояснення підкапотних механізмів, інколи переклади документації. Аудиторія невелика — півтори тисячі підписників, переважно архітектори, тех-ліди й люди, які хочуть ними стати. Я писав йому ще місяць тому. Він довго мовчав, потім написав: «Гаразд, але тільки за обідом. У мене вікно у середу між зустрічами. Прийдете на Хрещатик о другій?».
У середу о другій я знайшов його у вузькій кав'ярні третього поверху бізнес-центру, схованій від вулиці так, що про неї знають тільки ті, кому її показали. На вікнах — рослини, на столах — світлові плями від низького квітневого сонця, у динаміках — Бетховен на тлі. Богдан сидів біля вікна, із закритим ноутбуком і тарілкою грибного супу перед ним. Сорок п'ять років, лисуватий, у сірій кашеміровій сорочці без жодної емблеми.
— Сідайте. Раджу їхній том ям. Вони чомусь добре готують азійське, хоча тут увесь центр стилізований під єврейський.
Я замовив. Кельнерка пішла. Богдан подивився на мене з тим виразом, який буває у людей, що звикли пояснювати складні речі за обідом.
— Ну, з чого? Я зрозумів, що ви хочете від мене розмови про модель даних. Я надішлю вам потім посилання на свої статті, де я це розписую систематично. Але за столом — я можу дати тільки головну тезу. Готові?
— Готовий.
— 1С не є ORM, — сказав він просто. — Не плутайте дві речі: технічний прийом і архітектурний підхід. ORM — це технічний прийом мапування об'єктів на реляційні таблиці. Hibernate, Doctrine, Eloquent — усі вони працюють у парадигмі: «у мене є код з класами, у мене є БД з таблицями, мені треба, щоб одне ↔ інше». 1С робить інше. Вона не мапить. Вона вбудовує бізнес-онтологію у виконуване середовище. Це інший рівень абстракції.
— Що це означає на практиці?
— Це означає, що Hibernate не знає, що таке «регістр накопичення». Для нього регістр — це просто ще одна таблиця, і він її мапить як будь-яку іншу. У 1С — регістр накопичення — це семантичний примітив платформи. Платформа знає, що це таблиця фактів з періодом, що з неї треба будувати агрегати залишків і оборотів, що під час її використання треба ставити блокування при проведенні, що до неї є віртуальні таблиці підсумків. Усе це — не у вашому коді. Усе це — у платформі. Ви просто користуєтесь типом.
— А що ще такого є у платформі?
— Чотирнадцять різних типів. Кожен — окрема онтологічна категорія.
Він почав перераховувати, відсуваючи тарілку убік.
— Довідники. Нормативно-довідкова інформація. Контрагенти, номенклатура, валюти, склади. Це не просто список — це ієрархічна структура з групами, з підпорядкованими довідниками, з передвизначеними елементами, які платформа сама створює і зберігає при оновленні конфігурації. Hibernate не знає, що таке «передвизначений елемент». Для нього це просто рядок у таблиці. У 1С це — об'єкт, посилання на який можна жорстко прописати у коді, бо платформа гарантує, що він є.
— Документи. Події в обліку. Кожен документ — подія у часі, з датою, номером, автором, табличною частиною, проведенням (запис рухів у регістри) і відкочуванням (видалення рухів). Hibernate об'єкти не мають вбудованого життєвого циклу «провів — відкотив». У 1С — це фундаментальний механізм. Документ або проведений, або не проведений. Це не просто прапорець у БД. Це семантика обліку.
— Журнали документів. Перегляди по кількох типах документів одразу — щоб бухгалтер бачив у одному списку реалізації, надходження, повернення. Окрема онтологічна сутність, не таблиця, не подання — а семантичний агрегатор.
— Перелічення. Фіксований набір значень з гарантією, що ці значення не зміняться без оновлення конфігурації. Так само як enum у Java, тільки на рівні платформи, з можливістю використовувати як параметри методів, у запитах, у формах.
— Регістри накопичення. Таблиці фактів. Те, про що ви вже говорили з Юрієм минулого тижня — він вам розповідав. Я повторюся, але деталь: у платформі є дві віртуальні таблиці, які платформа агрегує заздалегідь — Остатки (стан на дату) і Обороты (зміни за період). Це не запити поверх руху. Це окремо підтримувані агрегати, які платформа оновлює сама. Hibernate такого не вміє. Якщо ви хочете швидкі залишки у Hibernate — ви будуєте їх вручну.
— Регістри відомостей. Таблиці зрізів. На кожну комбінацію вимірів — поточне значення з історією. Платформа підтримує методи Получить() і ПолучитьПоследнее(), які повертають значення на дату. Знову ж таки — заздалегідь оптимізована онтологія.
— Регістри бухгалтерії. Спеціалізована форма для подвійного запису. У ній — дебет і кредит окремо, плани рахунків, субконто, валюта, кількість. Це не просто таблиця. Це онтологічна модель бухгалтерії, винесена на рівень платформи. Жоден ORM світу такого не має.
— Регістри розрахунку. Для зарплати. Періодичні нарахування з базою, з витісненнями, з періодом дії. Окрема онтологія, побудована під специфіку нарахування заробітної плати з її витісненням, перерахунками і базовими алгоритмами.
— Плани рахунків. Кореневі довідники, спеціалізовані під бухгалтерію. З ієрархією, з типом активності, з реквізитами субконто.
— Плани видів характеристик. Динамічно розширювані набори атрибутів. Платформа дозволяє користувачу самому додавати «нові поля» до об'єкта, без перекомпіляції. У Hibernate це робиться через серіалізований JSON у колонку. У 1С — це рідний механізм, який платформа сама підтримує і відображає у формах.
— Бізнес-процеси і завдання. Графи переходів станів з чергами виконавців. Окрема онтологія для workflow. Не workflow-двигун окремий — а частина платформи.
— Підсистеми. Логічне групування об'єктів метаданих за функціональним призначенням. Платформа автоматично будує меню, права, обміни на основі підсистем.
— Ролі і права. Декларативне керування доступом на рівні об'єктів, реквізитів і навіть записів у таблицях (Row-Level Security вшита у платформу).
— Загальні модулі. Бібліотеки коду. Найближче до того, що ви розумієте під «звичайним кодом» у Hibernate.
Богдан сьорбнув холодний суп.
— Ось ваша онтологія. Чотирнадцять (плюс ще кілька) типів. Кожен — це семантично навантажений примітив. Не таблиця. Не клас. Концепція з предметної області бізнесу.
— А що це дає у порівнянні з ORM?
Богдан подумав.
— Швидкість на типовій задачі. Ось приклад. У Hibernate напишіть мені «отримати поточний залишок товару на складі на конкретну дату». Скільки рядків коду?
— Залежить. Я б писав щось через criteria queries, агрегації, віконні функції. Десь рядків п'ятнадцять-двадцять. Плюс міграція на створення табличних агрегатів, якщо хочу швидко.
— У 1С — один виклик. РегистрНакопления.ТоварНаСкладах.Остатки.Получить(Дата, Отбор). Усе. Платформа знає, як, платформа підтримує агрегат, платформа повертає число. Це різниця в порядок — за швидкістю розробки. І це не цукор. Це різниця в моделі думання.
— А що 1С робить ще?
— Журнал реєстрації — записує кожну зміну будь-якого об'єкта без вашого коду. Підписки на події — слухачі можуть реагувати на створення/зміну/видалення об'єктів. Обміни даними — вбудована синхронізація між базами через плани обмінів. Транзакційний контроль на рівні документів. Контроль посилань — об'єкт не можна видалити, поки на нього посилається інший. Контроль цілісності — платформа сама перевіряє типи у формах і у запитах.
— Hibernate цього не робить?
— Робить — але через окремі бібліотеки, кожна — окремий проект, з окремими залежностями, окремими версіями. У 1С — усе в коробці, узгоджене між собою. Ціна — несвобода. Вийти за рамки моделі неможливо.
— А що 1С не робить добре?
Богдан усміхнувся.
— Багато чого. По-перше, успадкування. У ORM ти можеш мати клас Person, від нього успадкувати Employee, Customer, Supplier. У класах метаданих 1С спадкування немає. Довідник Контрагенти не успадковується від чогось. Усі довідники — окремі рівноправні сутності. Є родитель (ієрархія груп у тому самому довіднику) і владелец (підпорядкування одного довідника іншому), але це посилання, не спадкування реквізитів. Якщо тобі треба «спільні поля для контрагента і співробітника» — копіюй або винось у регістр відомостей.
— По-друге, поліморфізм. У ORM ти можеш мати інтерфейс Auditable і будь-який клас, який його реалізує. У 1С такого немає. У платформи є обмежений механізм через «загальні реквізити», які додаються до груп об'єктів — але це не справжній поліморфізм. Це shared columns. Дрібна поступка.
— По-третє, гнучкі many-to-many. У ORM ти створюєш проміжну таблицю з reverse-mapping. У 1С ти робиш регістр відомостей — таблицю комбінацій вимірів. Якщо без періоду — це фактично проміжна таблиця, точний аналог @ManyToMany. Якщо з періодом — це M2M з історією станів, що сильніше за чистий @ManyToMany. Альтернатива — таблична частина, але вона завжди прив'язана до батьківського об'єкта, не самостійна сутність. Чого у 1С нема — це двостороннього симетричного доступу як у @ManyToMany: ти завжди працюєш через вимірі регістру, не через колекції в обох об'єктах.
— По-четверте, функціональні патерни. У Scala чи у Kotlin ти пишеш monads, comprehensions, lazy collections. У 1С ні цього, ні приблизного. Мова — імперативна, без функціональних патернів. Це обмеження, але це і свобода — менше способів вистрелити собі в ногу.
— По-п'яте, типізована полімовна композиція. У ORM у тебе є generics, type parameters, бавишся з типами. У 1С типізація — динамічна, перевірки — у runtime, generics немає. Якщо ти любиш List<UserDTO<Role>> — ти будеш страждати у 1С.
Він зробив паузу.
— Це інша мова в широкому сенсі. Не тільки синтаксично, а концептуально. Мова конкретної предметної області — бізнес-обліку. Усе, що поза ним, — або робиться через біль, або не існує взагалі.
— Філософське питання, — сказав я. — Що це? Чим ця штука є?
Богдан довго мовчав. Потім подивився мені прямо в очі.
— Це доменно-специфічний фреймворк, у якого предметна область — бухгалтерський і управлінський облік. У спеціалізованій літературі такі речі називають domain-specific framework або domain-driven framework. Не плутайте з DSL — це не мова, це фреймворк. У ньому є мова, але це частина фреймворку, не самостійна цінність.
— Бо 1С не виросла з універсальних інструментів. Вона виросла з обліку. У дев'яностих її проектували бухгалтери — не з нуля, але дуже близько. Ті, хто проектував, знали кореспонденцію рахунків, знали структуру балансу, знали, як закривається період. Вони закодували все це у платформу. Той факт, що з 2003-го з'явилась мова запитів, з 2009-го з'явились керовані форми, з 2013-го з'явились мобільні — це надбудови, що пристосовують ту саму онтологію до нових технологічних умов. Сама онтологія — від 1990-х. Вона має ту глибину, яку ні Hibernate, ні Salesforce, ні Odoo не мають у цьому конкретному домені.
— А в інших доменах?
— Поза цим — нуль. 1С не є хорошим фреймворком для веб-додатку. Не є хорошим для real-time системи. Не є хорошим для AI-pipelines. Це фреймворк бухгалтерії. Він гнеться до сусідніх задач — управлінський облік, логістика, виробничий облік — але всі вони — варіації на тему обліку. Як тільки виходиш за межі — платформа стає тягарем.
— А мова запитів? Це чистий SQL?
— Ні, — Богдан хитнув головою. — Це власний діалект, побудований над метамоделлю. Зовні схожий на SQL: ВЫБРАТЬ ... ИЗ ... ГДЕ ... СГРУППИРОВАТЬ ПО .... Але під капотом — це не SQL. Платформа транслює запит у SQL конкретної DBMS — SQL Server, PostgreSQL, Oracle, DB2. Розробник пише один раз — платформа адаптує під кожен діалект сама.
— А це працює нормально?
— Працює дивно. По-перше, немає JOIN у вашому коді. Замість JOIN ви пишете точкою: Реализация.Контрагент.Наименование. Платформа сама будує LEFT JOIN на довідник Контрагенти. Це елегантно, але приховує план виконання — і саме звідси беруться повільні запити, про які вам казав Юрій.
— По-друге, віртуальні таблиці. Я вже казав про них. У запитах 1С ти можеш писати ИЗ РегистрНакопления.ТоварНаСкладах.Остатки(&Дата) КАК Залишки — і платформа сама бере агрегат на цю дату, без сканування рухів. У чистому SQL такого механізму немає. Тут — є.
— По-третє, тимчасові таблиці у запитах. Ти можеш у запиті 1С створити іменовану тимчасову таблицю (ПОМЕСТИТЬ) і використати її далі. Платформа сама вирішить, як це реалізувати — як CTE, як #temp, як inline subquery — залежно від DBMS.
— Це SQL з покращеннями для обліку. Дуже сильний інструмент. Не такий потужний, як справжній SQL (немає віконних функцій, обмежено MERGE, немає LATERAL), але сфокусований саме під онтологію.
— Підсумок, — сказав я. — Як ти скажеш молодому розробнику, який питає: «що це таке»?
Богдан зробив паузу.
— Я б сказав: 1С — це не Java, це не PHP, це не Salesforce, це не Odoo. Це окрема категорія. Domain-specific framework для пострадянського бухгалтерського обліку, який виріс у платформу для пострадянського бізнес-обліку загалом, і яка зараз намагається бути платформою бізнес-додатків у широкому сенсі — з різним успіхом.
— Ваше завдання як розробника — зрозуміти онтологію. Не вивчити синтаксис — це найлегше. А зрозуміти як платформа думає про бізнес. Що таке проводка, що таке регістр, що таке період, що таке зрізи. Це не технологія — це філософія обліку, закодована у платформі. Хто це засвоїв — пише на 1С швидко й елегантно. Хто не засвоїв — бореться з платформою все життя.
— А коли ви розумієте, що ви засвоїли?
— Коли перестаєте писати запит у циклі.
Я записав цю фразу.
Богдан розрахувався, ми вийшли на вулицю. Сонце вже хилилось до Майдану. Він простягнув мені візитку — звичайний картон з телефоном, без логотипу, без сайту.
— Якщо у книзі потрібен ще раз — пишіть. Я живу у Києві, мене легко знайти.
На Хрещатику грав перший гітарист сезону — старший чоловік біля магазину «Подоляночка», виводив вальси Бетховена нерівно, але з душею. Я йшов до метро, і у голові оберталась одна фраза:
«Зрозумій, як платформа думає про бізнес».
Це формула, яка пояснює мало не все, що я почув за всі попередні розділи. Антон, який двадцять років всередині цієї моделі — він не запам'ятав мову; він засвоїв онтологію. Юрій, який лагодить повільні запити — він знає, як платформа транслює крапку у JOIN. Олександр, який тримає чотирьох клієнтів — він розуміє, що одна онтологія працює крізь чотири рантайми. Володимир Васильович, у якого DOS-машина з шістою версією — він пам'ятає, як ця онтологія народжувалася і нашаровувалася.
Усі вони — частини однієї знаннєвої спільноти. Спільноти, у якій вхід дешевий (один курс — і ти усередині), але глибина — на десятиліття. Спільноти з власною мовою, власною онтологією, власною економікою.
На цьому закривається Частина II. Ми пройшли всередину платформи — від метаданих через мову, через продуктивність, через клієнтів, до моделі даних. Ми побачили, що 1С — це не одна технологія, а система технологій, об'єднана онтологією бізнес-обліку. Не магія. Не іграшка. Серйозний інженерний продукт зі своїми сильними і слабкими сторонами.
Частина III, яка починається з наступного розділу, — про практику. Як писати на цій платформі ефективно, як уникати найпоширеніших помилок, як працювати з типовими конфігураціями, як інтегрувати Git у процес, як тестувати — у світі, у якому ніхто не тестує. Це найприкладніша частина книги. Її можна читати окремо, без попередніх — якщо ви розробник і хочете відразу до справи.
Наступний розділ — про типові конфігурації. Про екосистему продуктів, які тримають весь український бізнес. Розділ десятий.