Знакомьтесь, Анна Ивановна
У меня есть пет-проект — [Анна Ивановна](https://github.com/kireevys/anna_ivanovna), Личный Бухгалтер.
Он вырос из эксель-таблички, и теперь это настольное таури-приложение, которое помогает мне вести личные финансы. А ещё это моя персональная лаба, где я обкатываю различные архитектурные приёмы и подходы.
В частности, основной постулат проекта — «Защита ядра от изменений»: какие бы сценарии я ни выдумывал, мой бизнес-функционал должен оставаться неизменным как можно дольше. Открытым к расширению, но закрытым к изменениям. И я всячески стараюсь усложнить себе задачу.
Как основу я беру принципы чистоты — Functional Core / Imperative Shell ( #FCIS ) — по сути отделение бизнес-логики (БЛ) от IO, а также слоистую архитектуру. Мне не нравится обзывать такие подходы луковыми/гексагональными и прочими — скажу, что я стараюсь быть ближе к Границам Контекстов из #DDD по Эрику Эвансу.
Если интересен проект — заходите на гитхаб, почитайте ридми. В дальнейшем я буду добавлять подробности о проекте и его истории.
#buh_anna #аннаивановна
У меня есть пет-проект — [Анна Ивановна](https://github.com/kireevys/anna_ivanovna), Личный Бухгалтер.
Он вырос из эксель-таблички, и теперь это настольное таури-приложение, которое помогает мне вести личные финансы. А ещё это моя персональная лаба, где я обкатываю различные архитектурные приёмы и подходы.
В частности, основной постулат проекта — «Защита ядра от изменений»: какие бы сценарии я ни выдумывал, мой бизнес-функционал должен оставаться неизменным как можно дольше. Открытым к расширению, но закрытым к изменениям. И я всячески стараюсь усложнить себе задачу.
Как основу я беру принципы чистоты — Functional Core / Imperative Shell ( #FCIS ) — по сути отделение бизнес-логики (БЛ) от IO, а также слоистую архитектуру. Мне не нравится обзывать такие подходы луковыми/гексагональными и прочими — скажу, что я стараюсь быть ближе к Границам Контекстов из #DDD по Эрику Эвансу.
Если интересен проект — заходите на гитхаб, почитайте ридми. В дальнейшем я буду добавлять подробности о проекте и его истории.
#buh_anna #аннаивановна
Переезд на самописный TEA (The Elm Architecture) движок
Сегодня — заметка о том, как я затащил #FCIS - Functional Core / Imperative Shell на фронт #аннаивановна. Выделил под Yew собственный #TEA движок, чтобы получить чистое разделение Core/Shell. Рассказываю, что сработало, что заболело и за что пришлось заплатить.
ПР: https://github.com/kireevys/anna_ivanovna/pull/66/changes
Немного истории.
По задумке, Анна Ивановна может работать в нескольких режимах:
- Персональное настольное приложение — только локальная база данных, без синхронизации и с полным контролем конфиденциальности
- Веб-сервис — только облако, доступ через браузер, все данные на стороне владельца сервиса — классический веб-сервис
- Комбинированный режим — доступ через настольное приложение с облачной синхронизацией — это позволяет работать и с веб-сервисом через браузер, и без интернета через настолку, и с веб-сервисом с настолки
- Бонусом — веб-сервис из телеграма
Конечно, я не собирался делать разный фронт для каждого клиента, и мой выбор пал на TEA архитектуру — по сути FCIS для фронта.
В #rust есть Yew-фреймворк — для меня он был вне конкуренции: минималистичный дизайн, полнота, и прямая заявка на чистый TEA:
Ради приличия я посмотрел на Leptos и Dioxus, но в моменте больше понравился Yew.
Я затащил его на проект, сделал фронтенд, ещё практически read-only — мне понравилось. server-side render, html-макрос, компоненты — очень удобно.
При имплементации фичи Редактирования Плана я заметил, что корневой метод обработки сообщений
Я не устоял перед искушением — в первой имплементации Редактора Плана я безжалостно мутировал стейт.
Мутации — коварный паттерн. Реализуются элементарно: берёшь и изменяешь. Это просто работает. Однако это усложняет отслеживание потока выполнения — никогда не знаешь, что и где должно мутировать, а что мутировать не должно. Именно поэтому TEA имеет ровно одно место для изменения стейта — после вычисления новой модели, когда решение уже принято. Только так можно отделить IO от принятия решения и сохранить бизнес-логику (БЛ) в чистоте.
Также мутирующий стейт очень сложно тестировать — никогда не знаешь, где, что и когда мутирует.
Мне, конечно, не нравилось, фича была сложной и объемной, имплементация была довольно грязной, тесты мне было писать противно. Базовая задача - редактирование плана - была решена!
Любая грязь требует вернуть должок.
Недавно мой брат нашёл баг — можно было сделать отрицательное пополнение.
У меня давно выработался алгоритм работы с багами:
1. Воспроизвести на автоматизированном тестировании
2. Исправить
Но писать тесты к грязному коду — занятие для ценителей.
Требовался рефакторинг.
Мне абсолютно не понравился подход yew к мутациям, и я решил выделить свой простенький движок для TEA — по сути два трейта: один для моделей и Functional Core, второй для IO — Imperative Shell.
⬇️
Сегодня — заметка о том, как я затащил #FCIS - Functional Core / Imperative Shell на фронт #аннаивановна. Выделил под Yew собственный #TEA движок, чтобы получить чистое разделение Core/Shell. Рассказываю, что сработало, что заболело и за что пришлось заплатить.
ПР: https://github.com/kireevys/anna_ivanovna/pull/66/changes
Немного истории.
По задумке, Анна Ивановна может работать в нескольких режимах:
- Персональное настольное приложение — только локальная база данных, без синхронизации и с полным контролем конфиденциальности
- Веб-сервис — только облако, доступ через браузер, все данные на стороне владельца сервиса — классический веб-сервис
- Комбинированный режим — доступ через настольное приложение с облачной синхронизацией — это позволяет работать и с веб-сервисом через браузер, и без интернета через настолку, и с веб-сервисом с настолки
- Бонусом — веб-сервис из телеграма
Конечно, я не собирался делать разный фронт для каждого клиента, и мой выбор пал на TEA архитектуру — по сути FCIS для фронта.
В #rust есть Yew-фреймворк — для меня он был вне конкуренции: минималистичный дизайн, полнота, и прямая заявка на чистый TEA:
Developers who have experience with frameworks like React and Elm should feel quite at home when using Yew.
Ради приличия я посмотрел на Leptos и Dioxus, но в моменте больше понравился Yew.
Я затащил его на проект, сделал фронтенд, ещё практически read-only — мне понравилось. server-side render, html-макрос, компоненты — очень удобно.
При имплементации фичи Редактирования Плана я заметил, что корневой метод обработки сообщений
trait Component::update принимает мутирующую ссылку на &mut self. В моём понимании TEA мы не должны мутировать стейт — суть компонента в том, что мы каждый раз выдаём новое состояние на базе текущего и поступивших команд.Я не устоял перед искушением — в первой имплементации Редактора Плана я безжалостно мутировал стейт.
Мутации — коварный паттерн. Реализуются элементарно: берёшь и изменяешь. Это просто работает. Однако это усложняет отслеживание потока выполнения — никогда не знаешь, что и где должно мутировать, а что мутировать не должно. Именно поэтому TEA имеет ровно одно место для изменения стейта — после вычисления новой модели, когда решение уже принято. Только так можно отделить IO от принятия решения и сохранить бизнес-логику (БЛ) в чистоте.
Также мутирующий стейт очень сложно тестировать — никогда не знаешь, где, что и когда мутирует.
Мне, конечно, не нравилось, фича была сложной и объемной, имплементация была довольно грязной, тесты мне было писать противно. Базовая задача - редактирование плана - была решена!
Любая грязь требует вернуть должок.
Недавно мой брат нашёл баг — можно было сделать отрицательное пополнение.
У меня давно выработался алгоритм работы с багами:
1. Воспроизвести на автоматизированном тестировании
2. Исправить
Но писать тесты к грязному коду — занятие для ценителей.
Требовался рефакторинг.
Мне абсолютно не понравился подход yew к мутациям, и я решил выделить свой простенький движок для TEA — по сути два трейта: один для моделей и Functional Core, второй для IO — Imperative Shell.
pub trait Model: Clone {
type Msg;
type Cmd;
fn handle(self, msg: Self::Msg) -> (Self, Vec<Self::Cmd>);
}
pub trait Shell<M: Model> {
fn execute(&self, cmd: M::Cmd);
}
Model::handle принимает и возвращает себя по значению (никаких `&mut`), плюс отдаёт список команд для Shell. Shell — это IO: получает команды и выполняет их в реальном мире. Классический Elm — ядро не делает IO, ядро описывает, что должно произойти.⬇️
⬆️
Всю фронтовую логику изменения стейтов я перенёс на этот движок, модели отделил от yew и их презентации, а yew оставил как рантайм — теперь он запускает движок, роутит в него сообщения и рендерит получившийся стейт с помощью презентера.
Это позволило мне написать generic-стенд для тестирования как каждой модели в отдельности, так и всего App в сборе — там получилась матрёшка, которая роутит сообщения вглубь себя.
Тестирование сводится к проверке движка этим стендом и сериализации/десериализации Модели в html и Msg в структуры.
Дополнительно я получил возможность заменить yew на что угодно без изменения движка — в любой момент можно сделать tui/tauri-ipc/gpui. Я не собираюсь этого делать, но мне греет душу такая возможность.
Бонусом — чистый borrowing:
За это я заплатил потенциальной потерей стейта в
Сейчас всё ещё довольно грязновато — презентеры сами по себе делают валидации полей, но у меня есть тикет https://github.com/kireevys/anna_ivanovna/issues/64 с идеей исправления этой грязи.
TEA без owned-семантики — это вообще не TEA. Суть подхода в том, что
Это первый большой пост в этом канале. Расскажите, как вы относитесь к #FCIS? Применяли на своих проектах — где именно и с каким результатом? Будет интересно почитать в комментариях.
Спасибо, что дочитали до конца!
#buh_anna #аннаивановна
Всю фронтовую логику изменения стейтов я перенёс на этот движок, модели отделил от yew и их презентации, а yew оставил как рантайм — теперь он запускает движок, роутит в него сообщения и рендерит получившийся стейт с помощью презентера.
Это позволило мне написать generic-стенд для тестирования как каждой модели в отдельности, так и всего App в сборе — там получилась матрёшка, которая роутит сообщения вглубь себя.
Тестирование сводится к проверке движка этим стендом и сериализации/десериализации Модели в html и Msg в структуры.
Дополнительно я получил возможность заменить yew на что угодно без изменения движка — в любой момент можно сделать tui/tauri-ipc/gpui. Я не собираюсь этого делать, но мне греет душу такая возможность.
Бонусом — чистый borrowing:
app.model = handle(app.model, msg). Стейт не копируется, а передаётся во владение. С yew-овским &mut self так было нельзя — там можно только править на месте, а забрать и вернуть нельзя. По сути получился zero-cost handling.За это я заплатил потенциальной потерей стейта в
Option<AppModel> и take(), чтобы вытащить модель по владению. Если handle запаникует, модель останется None, и view покажет render_fatal. Неидеально, но честно: паника в чистой функции — это баг, который должен быть виден, а не замят.Сейчас всё ещё довольно грязновато — презентеры сами по себе делают валидации полей, но у меня есть тикет https://github.com/kireevys/anna_ivanovna/issues/64 с идеей исправления этой грязи.
TEA без owned-семантики — это вообще не TEA. Суть подхода в том, что
handle(self) → Self передаёт модель во владение и возвращает новую: стейт меняется ровно в одном месте, и больше нигде это невозможно. Rust тут ничего не «чинит» — он просто удобная среда для такой семантики: borrow checker сам не даст никому другому дотянуться до модели. Yew этого не позволяет, пришлось выделить свой движок. Плата — один Option в рантайме; выигрыш — чистый Core, тестируемый в изоляции, и zero-cost handling. Для меня это был очередной шаг в сторону FCIS — подхода, в который я верю и на котором обкатываю Анну Ивановну.Это первый большой пост в этом канале. Расскажите, как вы относитесь к #FCIS? Применяли на своих проектах — где именно и с каким результатом? Будет интересно почитать в комментариях.
Спасибо, что дочитали до конца!
#buh_anna #аннаивановна
🔥4
Please open Telegram to view this post
VIEW IN TELEGRAM
YouTube
Pixies - Where is my mind (Красный Металлург)
Голова Алёна - вокал,
Паранченко Никита - гитара (преподаватель Анастасия Зелёная),
Киреев Юрий- гитара (преподаватель Анастасия Зелёная),
Бодяновская Анастасия - бас-гитара (преподаватель Куляба Роман),
Струкова Любовь - барабаны (преподаватель Черний Вадим).…
Паранченко Никита - гитара (преподаватель Анастасия Зелёная),
Киреев Юрий- гитара (преподаватель Анастасия Зелёная),
Бодяновская Анастасия - бас-гитара (преподаватель Куляба Роман),
Струкова Любовь - барабаны (преподаватель Черний Вадим).…
🔥3
Отыграли летний отчетник с @red_mettallurg_krd и Дима Чернат уже собрал альбом 🐱
https://dmitrychernat.wfolio.pro/disk/25-07-2026-8-oy-otchetnyy-kontsert-krasnyy-metallurg-srjswg
https://dmitrychernat.wfolio.pro/disk/25-07-2026-8-oy-otchetnyy-kontsert-krasnyy-metallurg-srjswg
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Похоже, моя Лиза от меня устала и решила отправить в мир по настоящему легендарной и тяжёлой литераторы 🫦
6 издание, 2026 год
6 издание, 2026 год
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5
Я не мастер фотографии, но вот вам немного веловайба.
Вообще, кажется это лучший подарок в моей жизни)
Вообще, кажется это лучший подарок в моей жизни)
🔥6