{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Дима пишет | Дмитрий Смотров: заметки с тегом АрхитектураПО",
    "_rss_description": "backend python fastapi django rabbitmq kafka postgresql sqlalchemy",
    "_rss_language": "ru",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": "",
    "_itunes_explicit": "",
    "home_page_url": "https:\/\/dimasmotrov.ru\/tags\/arhitekturapo\/",
    "feed_url": "https:\/\/dimasmotrov.ru\/tags\/arhitekturapo\/json\/",
    "icon": "https:\/\/dimasmotrov.ru\/pictures\/userpic\/userpic@2x.jpg?1718470292",
    "authors": [
        {
            "name": "Дима Смотров",
            "url": "https:\/\/dimasmotrov.ru\/",
            "avatar": "https:\/\/dimasmotrov.ru\/pictures\/userpic\/userpic@2x.jpg?1718470292"
        }
    ],
    "items": [
        {
            "id": "39",
            "url": "https:\/\/dimasmotrov.ru\/all\/probilsya-na-habr\/",
            "title": "Пробился на хабр",
            "content_html": "<p>Написал свою первую статью на хабре, посвященную техническим аспектом из <a href=\"https:\/\/dimasmotrov.ru\/all\/ya-tut-proekt-pod-klyuch-realizoval\/\">поста<\/a> про реализованную мной функциональность красивой работы с данными.<\/p>\n<p>Прикольный опыт редактора более менее серьезного материала. Текст старался держать больше в литературном стиле, нежели в сухом техническом, так что читаться будет легко.<\/p>\n<p>Почитать можно тут — <a href=\"https:\/\/habr.com\/ru\/companies\/astralinux\/articles\/972328\/\">https:\/\/habr.com\/ru\/companies\/astralinux\/articles\/972328\/<\/a> . Там как раз есть пара спорных моментов, на мой взгляд, с удовольствием подхвачу накидывание на вентилятор. Так что задавайте ваши вопросы :)<\/p>\n",
            "date_published": "2025-12-02T19:23:53+03:00",
            "date_modified": "2025-12-02T19:23:49+03:00",
            "tags": [
                "DataWarehouse",
                "АрхитектураПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Tue, 02 Dec 2025 19:23:53 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "39",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "36",
            "url": "https:\/\/dimasmotrov.ru\/all\/12-faktorov-chast-4\/",
            "title": "12 факторов. Часть 4",
            "content_html": "<p>10) <b>Паритет разработки\/работы приложения. Держим среду разработки идентичной среде развертывания. <\/b>Если на проде используется Postgres, то локально мы тоже используем Postgres, никаких SQLite\/Oracle\/MySQL, иначе вы очень рискуете стать тем самым «а у меня локально все работает» парнем. На моей практике различие версии одной зависимости между стейджем и продом привели к двухнедельному кранчу. Фактор обеспечивает способность непрерывного развертывания приложения за счет минимизации различий между средами разработки и исполнения.<br \/>\n11) <b>Журналирование. Логи как поток событий.<\/b> Приложение отдает контроль за своими логами на аутсорс стороннему инструментарию(привет <i>ELK<\/i>, а если точнее, то <i>logstash<\/i>). Каждый процесс пишет логи в стандартный вывод, а там оно уже как-нибудь «само». Почему? Опять же: для простоты масштабирования. Еще важно подчеркнуть — логи рассматриваются как <i>поток событий<\/i>(одна строка логов — одно событие системы), из которых формируется журнал. <i>Журнал<\/i> — это поток агрегированных, упорядоченных по времени событий, собранных из потоков вывода всех запущенных процессов и вспомогательных сервисов. По сути это такой же текстовый файл, но его можно визуализировать для удобства(привет <i>Kibana<\/i>).<br \/>\n12) <b>Администрирование<\/b>. Выполняем администрирование в разовых процессах, причем среда выполнения таких процессов идентична среде выполнения всего приложения. К администрированию относится выполнение миграций, запуск разовых скриптов(например пользака с админскими правами создать), и тому подобное. Очень сильно перекликается с 10 пунктом и нужно по тем же причинам: если мы будем запускать приложение в одной среде, а администрировать приложение в другой, мы точно в конечном итоге нарвемся на рассогласование конфигураций. Хранить разовые скрипты рекомендуется рядом с основным кодом приложения.<\/p>\n",
            "date_published": "2025-09-14T22:57:25+03:00",
            "date_modified": "2025-09-14T22:57:23+03:00",
            "tags": [
                "АрхитектураПО",
                "ОтказоустойчивоеПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Sun, 14 Sep 2025 22:57:25 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "36",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "35",
            "url": "https:\/\/dimasmotrov.ru\/all\/12-faktorov-chast-3\/",
            "title": "12 факторов. Часть 3",
            "content_html": "<div class=\"e2-text-picture\">\n<img src=\"https:\/\/dimasmotrov.ru\/pictures\/image_2025-08-16_23-52-57.png\" width=\"405\" height=\"389\" alt=\"\" \/>\n<\/div>\n<p>7) <b>Привязки портов.<\/b> Сервисы экспортируются через бинд порта — то есть сервис тусит на порту и слушает запросы. Тут камень в огород способа развертывания приложения как дополнительного модуля к веб серверу, существовавшему ранее. Сейчас же будьте добры делать приложение самодостаточным, идущем в комплекте с собственным веб-сервером(который вы объявите в зависимостях), а не подсовывайте сервер извне.<br \/>\n8) <b>Параллельное выполнение команд.<\/b> И для параллельного выполнения используются процессы. За основу взяли все хорошее из модели Unix процессов + разделение на типы процессов: если сервис будет обрабатывать HTTP-запросы и выполнять задачи в фоне, то сервис должен быть разделен на веб-процесс и процесс фоновых задач. Таким образом, если увеличивается нагрузка на веб-процесс, то мы докидываем дополнительный веб процесс. Аналогично для процесса фоновых задач. На картинке можно наглядно посмотреть. Вот тут <a href=\"https:\/\/adam.herokuapp.com\/past\/2011\/5\/9\/applying_the_unix_process_model_to_web_apps\/\">пост<\/a> сооснователя heroku с предложением данной концепции.<br \/>\n9) <b>Утилизируемость<\/b>. Приложение должно запускаться и останавливать свою работу в любой момент, когда это потребуется. В идеале мы минимизируем время запуска, а процесс завершения делаем безопасным — завершаем текущую сессию с БД, чтобы соединения не текли, завершаем все фоновые задачи, только после этого процесс умирает. Хорошо бы еще добавить <i>устойчивость к внезапной смерти оборудования<\/i>, но подобное событие считается менее вероятным, чем простое завершение работы по SIGTERM. Весь этот фактор направлен на то, чтобы автомасштабирование работало по принципу «включил\/выключил», как свет на кухне: нужно — добавили подов, ненужно — убрали.<\/p>\n",
            "date_published": "2025-09-04T16:42:49+03:00",
            "date_modified": "2025-09-04T16:42:43+03:00",
            "tags": [
                "АрхитектураПО",
                "ОтказоустойчивоеПО",
                "Разработка"
            ],
            "image": "https:\/\/dimasmotrov.ru\/pictures\/image_2025-08-16_23-52-57.png",
            "_date_published_rfc2822": "Thu, 04 Sep 2025 16:42:49 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "35",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": [
                    "https:\/\/dimasmotrov.ru\/pictures\/image_2025-08-16_23-52-57.png"
                ]
            }
        },
        {
            "id": "34",
            "url": "https:\/\/dimasmotrov.ru\/all\/12-faktorov-chast-2\/",
            "title": "12 факторов. Часть 2",
            "content_html": "<p>Прошлый <a href=\"https:\/\/dimasmotrov.ru\/all\/12-faktorov-chast-1\/\">пост<\/a><\/p>\n<p>4) <b>Сторонние службы<\/b>. Это службы, доступные приложению по сети. База данных, очереди сообщений, кеши — все это сторонние службы и они должны быть *подключаемыми ресурсами*, доступными по URL-адресу, который в свою очередь заносится через параметры конфигурации. В случае, если сервер сторонней службы начинает барахлить, служба переносится на другой сервер и приложению дают URL с новым сервером.<br \/>\n5) <b>Сборка, релиз и выполнение<\/b>. Жесткое разделение этапов развертывания приложения на сборку(берем код и «компилируем» его: ставим зависимости в окружение; реально компилируем, если ЯП проекта компилируемый), релиз(к «собранной» программе докидываются конфиги) и выполнение(непосредственно запуск программы) позволяет контролировать то, какой код был развернут на сервере с помощью систем контроля релиза(например Jenkins), которые в случае чего позволят откатить релиз и проанализировать проблемный релиз на предмет ошибок.<br \/>\n6) <b>Процессы<\/b>. Приложение запускается как один, либо как несколько процессов, без сохранения внутреннего состояния(сервис без состояния — *stateless*). Так же между процессами не должно быть разделяемых данных. Это нужно для упрощения горизонтального масштабирования — просто добавь ~~воды~~ еще один процесс. В случае, если у сервиса есть состояние(допустим, он кеш хранит в глобальном словаре), то нужно это состояние распространить и держать согласованным между всеми инстансами приложения. Состояние, если оно у вас есть, должно быть вынесено во внешнюю службу(для нашего примера для кеша использовать Redis\/Memcached).<\/p>\n",
            "date_published": "2025-08-06T09:49:43+03:00",
            "date_modified": "2025-08-06T09:49:38+03:00",
            "tags": [
                "АрхитектураПО",
                "ОтказоустойчивоеПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Wed, 06 Aug 2025 09:49:43 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "34",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "33",
            "url": "https:\/\/dimasmotrov.ru\/all\/12-faktorov-chast-1\/",
            "title": "12 факторов. Часть 1",
            "content_html": "<p>Был период, когда стандартов развертывания веб приложениях особых не было, каждый разворачивался как хотел, масштабировался как мог.  Появились докер, кубер и стали корпоративным стандартом:  заворачивай приложение в контейнер, настраивай деплой и автоматическое масштабирование и поехали.<\/p>\n<p>Так как появились стандартизированные инструменты, появились и рекомендации для разработки приложений с той целью, чтобы минимизировать проблемы с деплоем с помощью этих инструментов. Рекомендации эти обозвали «12 факторные приложения» или же «12 шагов к куберу».<\/p>\n<p>Кратко концепцию можно сформулировать так: если приложение не может запуститься одной командой на вашей рабочей тачке — вы не заскейлитесь в нужный момент, либо скейл будет сопровождаться танцем с бубном и шаманским горловым пением.<\/p>\n<p>Предлагаю рассмотреть часть пунктов и разберемся чем они полезны.<\/p>\n<p>1) <b>Кодовая база.<\/b> Одна кодовая база — одно приложение. Кодовая база ведется с помощью системы контроля версий, любой, на ваш вкус. Каждый разработчик в команде работает с одним и тем же кодом(изменения, внесенные разработчиком во время работы не противоречат этому пункту), на серверах запускается этот же код. Представьте, что у вас есть N друзей близняшек, одевающихся почти одинаково. Отличать их друг от друга смогут разве что родители(а смогут ли?).<br \/>\n2) <b>Зависимости.<\/b> Приложения явно указывает пакеты, которые нужны ему для работы через манифесты декларации зависимостей и  менеджер зависимостей. Таким образом мы избегаем ситуаций, когда в одной среде приложение работает корректно, а при запуске на другом компьютере приложение падает из-за отсутствия нужной библиотеки.<br \/>\n3) <b>Конфигурация<\/b>. Должна подкидываться приложению из переменных окружения. Если вы прибьете гвоздями креды для подключения к БД в коде, а потом поменяете пароль на сервере базы или же переместите базу на другой хост, то без патча в исходном коде приложения вы банально не заведетесь.<\/p>\n<p>В последующих постах пройдемся и по остальным 9 пунктам :)<\/p>\n",
            "date_published": "2025-08-03T21:25:44+03:00",
            "date_modified": "2025-08-03T21:25:38+03:00",
            "tags": [
                "АрхитектураПО",
                "ОтказоустойчивоеПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Sun, 03 Aug 2025 21:25:44 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "33",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "12",
            "url": "https:\/\/dimasmotrov.ru\/all\/cqrs-3\/",
            "title": "CQRS (3)",
            "content_html": "<p><b>Когда полезно использовать<\/b><\/p>\n<p>— В сложных доменных областях. Использование подхода позволит снизить сложность каждого конкретного контекста программы.<br \/>\n— Если в системе сильно различается частота запросов на чтение и запросов на БЛ составляющие системы. Например можно разместить Command & Queries в разных процессах, что позволит горизонтально масштабировать Queries часть системы, так как обычно запросов на чтение сильно больше запросов на запись.<br \/>\n— Если ваша система спроектирована по принципу EDA(Событийно-ориентированная архитектура). Событию системы соотносится конкретная модель данных. Позволяет БЛ не вытекать за пределы обработчика события.<br \/>\n— Когда вы собираетесь разделить базу данных на БД для записи и БД для чтения. Однако следует учитывать, что при разделение БД так же встает вопрос о конечной согласованности данных в базах.<\/p>\n<p><b>Когда использование не даст преимуществ<\/b><\/p>\n<p>— Модели Command & Queries совпадают. В таких случаях лучше использовать совместную(единую) модель.<br \/>\n— В случае, если доменная область неверно выделена в системе, то использование CQRS только запутает и без того слабо структурированный код.<\/p>\n",
            "date_published": "2024-12-06T19:48:09+03:00",
            "date_modified": "2024-12-06T19:48:05+03:00",
            "tags": [
                "CQRS",
                "АрхитектураПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Fri, 06 Dec 2024 19:48:09 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "12",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "11",
            "url": "https:\/\/dimasmotrov.ru\/all\/cqrs-2\/",
            "title": "CQRS (2)",
            "content_html": "<p>Вернемся к основной теме<\/p>\n<p>В проектах часто вижу, что ORM модель сущности используется во всех контекстах(доменных областях, которые по сути есть в программе, но по факту никто их явно не выделял) системы:<br \/>\n— как выходное отображение для ответа API;<br \/>\n— как DTO между разными модулями;<br \/>\n— для модуля работы с БД(orm модель по логике должно существовать только в рамках этого модуля);<br \/>\n— как модель для формирования тела сообщения в очередь сообщений.<\/p>\n<p>В каждом контексте из перечисленных выше используется одна и та же модель, несмотря на то, что данные модели избыточны в каждом случае примерно на 40-50%(цифры вывел эмпирически, сухой статистики у меня нет, к сожалению). Имеем жирную модель данных на все случаи жизни.<\/p>\n<p>CQRS предоставляет разделение жирной модели на Command model(модель для работы БЛ: создание, обновление, связывания с моделями других сущностей, и т. д.) & Query Model(отображаемая пользователю в запрашиваемом домене).<\/p>\n<p>Названия украдены из концепции Command Query Separation (<a href=\"https:\/\/martinfowler.com\/bliki\/CommandQuerySeparation.html),\">https:\/\/martinfowler.com\/bliki\/CommandQuerySeparation.html),<\/a> ключевая идея которой в том, что требуется разделять действия над моделью на две категории:<br \/>\n— Queries — возвращает результат, но не меняет состояние системы(методы без сторонних эффектов)<br \/>\n— Command — Изменяет состояние системы, но не возвращает состояние системы.<\/p>\n<p>Для каждой категории действий над сущностями приложения мы имеем конкретные модели. Если меняется логика создания сущностей — мы актуализируем модель для Command категории. Если меняется логика отображения пользователю — мы актуализируем модель для Query.<\/p>\n<p>Для каждой категории действии может быть несколько моделей с более конкретной направленностью под каждую доменную область(например списочная и информационная модель из примера в прошлом посте).<\/p>\n",
            "date_published": "2024-12-06T19:47:19+03:00",
            "date_modified": "2024-12-06T19:47:14+03:00",
            "tags": [
                "CQRS",
                "АрхитектураПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Fri, 06 Dec 2024 19:47:19 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "11",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "10",
            "url": "https:\/\/dimasmotrov.ru\/all\/cqrs-1\/",
            "title": "CQRS (1)",
            "content_html": "<p>Или же Command Query Responsibility Segregation<\/p>\n<p>Вообще, идея концепции состоит в разделение моделей данных на модели для чтения и на модели для записи. Но, как мне кажется, лучше смотреть чуть шире:<br \/>\nкаждый контекст системы должен обладать своим собственным состоянием, на каждое состояние необходима своя модель, содержащая информацию исключительно для работы конкретного контекста.<\/p>\n<p>Например: представим главную страницу сайта для продажи товаров. На этой странице отображена минимальная информация по товарам — фотография, название, цена. Но при переходе на страницу конкретного товара мы увидем более подробное описание товара, помимо цены, фотографии и названия — остаток на складе, производитель, страна поставки сырья и тому подобное.<\/p>\n<p>Логично, что для отображения товара на главной странице не нужна вся информация о товаре — ее мы посмотрим детально при желании. Отсюда напрашиваются две модели для сущности товара — списочное отображение(минимум данных) и информационное представление(максимум данных).<\/p>\n<p>Конечно можно использовать только модель для информационного отображения, ведь в ней находятся все данные о товаре, а на главной странице мы можем отображать только необходимый минимум. Но зачем нам кратно увеличивать трафик и сокращать скорость работы главный страницы, когда мы можем этого не делать? К тому же известно, чем больше кода, тем больше вероятность ошибиться: например использовать неверное поле для отображения, допустим вместо title использовать description. Пример смешной и легкий, но если подумать, то сокращение полей в модели снижает риск ошибки, так как поля description в модели не будет вовсе, на главное странице оно не нужно.<\/p>\n<p>По аналогии с главной страницы товаров, разделение большой(«жирной») модели на более конкретизированные для текущего контекста выполнения позволяет сделать код безопаснее — лишние данные отсутствуют, мы просто обречены использовать именно то, что нужно :)<\/p>\n",
            "date_published": "2024-11-14T23:50:02+03:00",
            "date_modified": "2024-11-14T23:49:45+03:00",
            "tags": [
                "CQRS",
                "АрхитектураПО"
            ],
            "_date_published_rfc2822": "Thu, 14 Nov 2024 23:50:02 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "10",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "9",
            "url": "https:\/\/dimasmotrov.ru\/all\/modulny-monolit-5\/",
            "title": "Модульный монолит (5)",
            "content_html": "<p>Хочу подвести итоги темы модульного монолита.<\/p>\n<p>1) Монолит — система, состоящая из одной единицы развертывания.<br \/>\n2) Монолит по умолчанию не комок грязи, в его определение такого нет в принципе<br \/>\n3) Модульный монолит — монолит, спроектированный с учетом модульности.<br \/>\n4) Модульный компонент — независим, автономен и имеет четкий интерфейс<\/p>\n<p>Стоит помнить особенности разработки и эксплуатации монолитных систем:<br \/>\n1) Более легкий порог вхождения для разработки — думаю, тут все понятно.<br \/>\n2) Запуская проект для локальной разработки — вы запускаете ВЕСЬ проект.  Был я на проекте, где с учетом размера кодовой базы и мощностей рабочего железа процесс локального развертывания мог занимать полчаса. Удовольствие очень сомнительное, скажу я вам. Желательно в таком случае иметь тестовые стенды, в которых вы можете подкинуть свои изменения и проверить выполненную работу на них.<br \/>\n3) Деплой монолита — это деплой всех его модулей. Опять же, с ростом кодовой базы процесс становится дольше, отсюда у разных компаний могут появляться свои системы сборки, с точечной оптимизацией процесса развертывания, свои отделы выпусков, которые будут следить за окончанием работ над каждым модулем и соблюдением режима тишины. Буду надеятся, что внутрь подобных систем сборки залить вам не придется :)<br \/>\n4) Иногда разработчикам двух разных модулей приходится ждать друг друга — ведь чтобы запустить сборку изменение второго разработчика, нужно будет дождаться сборки изменений первого. Обычно это касается работы в крупном энтерпрайзе.<br \/>\n5) Масштабируемость. Возможны ситуации, когда определенные модули потребляют ресурсов больше, чем все остальные. К сожалению, нельзя добавить мощностей только для нагруженных модулей, вернее вертикальное масштабирование сервера приложения позволит это сделать, но ВМ конечно. А при горизонтальном масштабировании, добавляя новый экземпляр приложения, мы так же закладываем ресурсы для модулей, которые не нуждаются в дополнительных ресурсах. Как итог — теряем деньги на лишние вычислительные мощности. Не факт, что подобные потери будут велики, но стоит это учитывать. (тут должна быть шутка про то, как AWS выставляет чек на крупную сумму, так как ты не отключил автомасштабирование облака).<br \/>\n6) Сломана нога — умирает весь человек. Если один из модулей падает с критической ошибкой, все приложение падает на пол, вся команда разрабов сидит и чинит прод на выходных, либо же просто сидит отдыхает, пока виновник торжества исправляет ситуацию, отбиваясь от дубликатов ошибок и нависшего над душой менеджера.<br \/>\n7) Использование сторонних технологий. В рамках монолита нельзя использовать разные ЯП(исключения есть, но не о них сейчас). Поэтому резко начать впихивать rust у вас не получится, как использовали php, так и используйте :)<\/p>\n",
            "date_published": "2024-11-05T01:13:11+03:00",
            "date_modified": "2024-11-05T01:13:10+03:00",
            "tags": [
                "АрхитектураПО",
                "модульный_монолит",
                "Разработка"
            ],
            "_date_published_rfc2822": "Tue, 05 Nov 2024 01:13:11 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "9",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "8",
            "url": "https:\/\/dimasmotrov.ru\/all\/modulny-monolit-4\/",
            "title": "Модульный монолит (4)",
            "content_html": "<p>Мы договорились, что модуль — это боевая единица, способная решать свою задачу в кооперации с другими модулями посредством интерфейсов.<\/p>\n<p>Для понимания того, как модульность выглядит на уровне приложения обратимся к схеме:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/dimasmotrov.ru\/pictures\/image.png\" width=\"1024\" height=\"403\" alt=\"\" \/>\n<\/div>\n<p>Оранжевый и сиреневый квадраты — это модули Бизнес Логики(БЛ).<br \/>\nЗеленые линии — это тоже модули, но модули технические.<\/p>\n<p>В контексте данной темы технические модули называют «слоями», а модули БЛ «модулями», так как чаще всего изменения и разработка касаются бизнес логики — добавляются новые фичи, изменяется поведение старых и тому подобное.<\/p>\n<p>На схеме отображено взаимодействия модулей бизнес логики с техническими слоями приложения. Каждый модуль реализует собственную функциональность, используя возможности слоев БД, логики приложения(обмен сообщений, запуск фоновых задач, ...) через контракты(интерфейсы).<\/p>\n<p>Мы можем изменить реализацию и способ хранения данных, но модуль «Orders» ничего не заметит, он как использовал нужные ему вызовы методов слоя БД, так и использует. Можем поменять визуальное представление сущностей модуля «Payments», модулю все равно, он отдает со своей стороны данные по контракту и получает объект-представление, как и было оговорено в контракте.<\/p>\n<p>Модули БЛ добавляются, приложение растет, полезных действия приложения становится больше, и еще длительное время проект спокойно развивается в рамках такой архитектуры.<\/p>\n",
            "date_published": "2024-11-05T01:11:45+03:00",
            "date_modified": "2024-11-05T01:11:32+03:00",
            "tags": [
                "АрхитектураПО",
                "модульный_монолит",
                "Разработка"
            ],
            "image": "https:\/\/dimasmotrov.ru\/pictures\/image.png",
            "_date_published_rfc2822": "Tue, 05 Nov 2024 01:11:45 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "8",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": [
                    "https:\/\/dimasmotrov.ru\/pictures\/image.png"
                ]
            }
        },
        {
            "id": "7",
            "url": "https:\/\/dimasmotrov.ru\/all\/modulny-monolit-3\/",
            "title": "Модульный монолит(3)",
            "content_html": "<p>Продолжим по модульный монолит.<\/p>\n<p>Если монолитная архитектура почти всегда скатывается в «комок грязи», а модульность должна нас от этого комка спасти. Давайте поймем, что такое «модуль».<\/p>\n<p>Модуль — независимый участок кода, содержащий все необходимое для выполнения только одного аспекта желаемой функциональности. Состоит из интерфейса и реализации.<br \/>\nИнтерфейс модуля выражает элементы, которые предоставляются и требуются модулем. Элементы, определенные в интерфейсе, могут быть обнаружены другими модулями.<br \/>\nРеализация содержит рабочий код, соответствующий элементам, объявленным в интерфейсе.<\/p>\n<p>Модулем как правило называют модуль бизнес-логики приложения(модуль почтовой рассылки), но так же есть «инфраструктурные» модули(модуль для работы с БД, очередью и так далее).<\/p>\n<p>Модуль должен обладать следующими свойствами:<br \/>\n1)  Независимостью — зависимости модуля сведены к минимуму и частота изменений «родительских» модулей минимальна. Так же снижаем сцепленность и увеличиваем связность.<br \/>\n2) SRP — модуль решает четко определенную задачу.<br \/>\n3) имеет четко определенный интерфейс — контракт для взаимодействия с другими модулями. Хороший контракт — непротиворечивый и минимальный. Интерфейс модуля должен быть стабильным(не подвергаться частому изменению).<\/p>\n",
            "date_published": "2024-11-04T23:56:10+03:00",
            "date_modified": "2024-11-04T23:56:05+03:00",
            "tags": [
                "АрхитектураПО",
                "модульный_монолит",
                "Разработка"
            ],
            "_date_published_rfc2822": "Mon, 04 Nov 2024 23:56:10 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "7",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "6",
            "url": "https:\/\/dimasmotrov.ru\/all\/sravnenie-koda-opros\/",
            "title": "Сравнение кода. Опрос.",
            "content_html": "<p>После долгого затишья хочу начать с простенького опроса.<br \/>\nДва примера ниже выполняют одну и ту же задачу. Какой вариант вам нравится больше? И почему? Напишите в комментах.<\/p>\n<p>Первый вариант:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">def _write_steps(\n    self,\n    steps: List[SoftwareProfileStep],\n    profile_dir_path: Path,\n) -&gt; None:\n    &quot;&quot;&quot;\n    Записать шаги профиля в файловое хранилище\n\n    Args:\n        steps: Список шагов профиля управления ПО.\n        profile_dir_path: Путь к директории профиля в файловом хранилище\n    &quot;&quot;&quot;\n    steps_j2 = []\n    for step in steps:\n        if step.type == SoftwareProfileStepType.PACKAGE:\n            step_var_uid = self._write_step_vars(\n                step, \n                profile_dir_path,\n            )\n            steps_j2.append(\n                self._step_package_template.format(\n                    action=step.package_action,\n                    step_vars=step_var_uid,\n                )\n            )\n        elif step.type == SoftwareProfileStepType.REPOSITORY:\n            steps_j2.append(\n                self._step_repo_template.format(\n                    step_uid=step.uid,\n                )\n            )\n\n    steps_list = list(map(str, steps_j2))\n    steps_str = self.__to_jinja_statement(\n        self._steps_template.format(\n            steps=steps_list,\n        )\n    )\n\n    with Path(profile_dir_path \/ &quot;steps.j2&quot;).open(&quot;w&quot;) as steps_file:\n        steps_file.write(steps_str)<\/code><\/pre><p>Второй вариант:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">def _write_steps(\n    self,\n    steps: List[SoftwareProfileStep],\n    profile_dir_path: Path,\n) -&gt; None:\n    &quot;&quot;&quot;\n    Записать шаги профиля в файловое хранилище\n\n    Args:\n        steps: Список шагов профиля управления ПО.\n        profile_dir_path: Путь к директории профиля в файловом хранилище\n    &quot;&quot;&quot;\n    steps_j2 = [\n        self.__fill_step_template(step)\n        for step in steps\n    ]\n\n    steps_list = list(map(str, steps_j2))\n    steps_str = self.__to_jinja_statement(\n        self._steps_template.format(\n            steps=steps_list,\n        )\n    )\n\n    with Path(profile_dir_path \/ &quot;steps.j2&quot;).open(&quot;w&quot;) as steps_file:\n        steps_file.write(steps_str)\n\n@staticmethod\ndef __fill_step_template(step: SoftwareProfileStep):\n    def _package_template(step: SoftwareProfileStep):\n        step_var_uid = self._write_step_vars(\n            step,\n            profile_dir_path,\n        )\n        return self._step_package_template.format(\n            action=step.package_action,\n            step_vars=step_var_uid,\n        )\n    def _repo_template(step: SoftwareProfileStep):\n        return self._step_repo_template.format(\n            step_uid=step.uid,\n        )\n\n    actions = {\n        SoftwareProfileStepType.PACKAGE: _packege_template,\n        SoftwareProfileStepType.REPOSITORY: _repo_template,\n    }\n    return actions.get(step.type)(step)<\/code><\/pre>",
            "date_published": "2024-11-04T23:55:09+03:00",
            "date_modified": "2024-11-04T23:55:02+03:00",
            "tags": [
                "АрхитектураПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Mon, 04 Nov 2024 23:55:09 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "6",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "highlight\/highlight.js",
                    "highlight\/highlight.css"
                ],
                "og_images": []
            }
        },
        {
            "id": "3",
            "url": "https:\/\/dimasmotrov.ru\/all\/otvet-na-vopros\/",
            "title": "Ответ на вопрос",
            "content_html": "<blockquote>\n<p><i>Я не видел еще модульных монолитов, как он должен выглядеть, чтоб считаться достаточно модульным ?) Как обеспечить low coupling ?)<\/i><\/p>\n<\/blockquote>\n<p>Приведу небольшой пример(код рабочий, можете поиграться)<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">from dataclasses import dataclass\nfrom typing import Any, Callable\n\n\n# Тип так же может предоставлять интерфейс, а его внутренности могут быть чем угодно.\n# В примере это упущенно, но в последней реализации костыльно показано, что это мы тоже используем.\n@dataclass()\nclass State:\n    values: list[Any]\n\n    def extract_value(self) -&gt; Any:\n        return self.values[0]\n\n    def __len__(self):\n        return len(self.values)\n    \n    def __getitem__(self, key):\n        return self.values[key]\n\n# predicates.py #\ndef is_done(state: State) -&gt; bool:\n    return len(state) == 1\n\ndef is_even(state: State) -&gt; bool:\n    return len(state) % 2 == 0\n\n# predicates.py #\n\n# transformers.py #\ndef transform(state: State) -&gt; State:\n    new_state = State([state[0] + state[1]] +  state[2:])\n    return new_state\n\ndef transform_by_head_plus_tail(state: State) -&gt; State:\n    new_state = State([state[0] + state[-1]] + state[1:len(state) - 1])\n    return new_state\n\n# transformers.py #\n\n# app.py #\n# &quot;типами&quot; ниже задан интерфейс для работы метода. По сути мы уже обеспечили модульность данного компонента.\ndef Iterate(\n    state: State,\n    is_done: Callable[[State], bool],\n    transform: Callable[[State], State]\n) -&gt; State:\n    if is_done(state):\n        return state\n    state = transform(state)\n    return Iterate(state, is_done,transform)\n\n\ndef iterable_factory(\n    iterate_func: Callable[..., State],\n    stop_predicate: Callable[[State], bool],\n    transform: Callable[[State], State],\n    ) -&gt; Callable:\n    def decorator(state: State) -&gt; State:\n        if not state:\n            return State([])\n        return iterate_func(state, stop_predicate, transform)\n    return decorator\n\n# app.py #\n\n\n# main.py #\n\nFoldL = iterable_factory(Iterate, is_done, transform)\nprint(FoldL(State([1,5,3,7,9])))\n\nDummyFoldL = iterable_factory(Iterate, is_even, transform_by_head_plus_tail)\nprint(DummyFoldL(State([i for i in range(101)])))\n\nGausFoldL = iterable_factory(Iterate, is_done, transform_by_head_plus_tail)\nprint(GausFoldL(State([i for i in range(101)])))\n\n\nStringFoldL = iterable_factory(Iterate, is_done, transform)\nresult = StringFoldL(State(\n    &quot;Вызываемые объекты, которые принимают другие вызываемые объекты в качестве аргументов,&quot;\n    &quot;могут указывать на то, что их типы параметров зависят друг от друга с помощью typing.ParamSpec.&quot;.split()\n))\nprint(result)\n\n# main.py #<\/code><\/pre><p>В данном примере я хотел достаточно маленьким кусочком кода показать, что конкретнее я имел ввиду в предыдущем посте.<\/p>\n<p>Давайте договоримся, что модулем мы будем называть сущность, которая группирует связанные по смыслу операции. В нашем случае мы имеем модулю: app.py, predicate.py, transformers.py и модуль для запуска main.py .<\/p>\n<p>По сути, модуль app.py — единая точка входа. Для метода Iterate требуются зависимости, принимающие на вход состояние(State) и возвращающие либо bool, либо новое состояние. В данном контексте типизация является интерфейсом. Тут же используется так называемая «функциональная инъекция зависимостей».<\/p>\n<p>Получается обобщенный программный компонент, который благодаря слабой связанности(за счет четких интерфейсов на типах) легко может заменить свои составляющие компоненты. Сложность кода измеряется как последовательная сумма сложностей компонентов этого кода.<\/p>\n<p>Подобный подход хорошо расширяется в большие проекты. Вы можете заметить «это же по сути полиморфизм!» и формально будете правы. Но понятие модульного монолита все же шире, пускай оно и включает в себя статический или динамический полиморфизм.<\/p>\n<p>Это был ответ на вопрос к <a href=\"https:\/\/dimasmotrov.ru\/all\/modulny-monolit-1\/\">предыдущей статье<\/a>.<br \/>\nЗадавайте вопросы в комментариях.<\/p>\n",
            "date_published": "2024-08-14T23:15:46+03:00",
            "date_modified": "2024-08-14T23:30:33+03:00",
            "tags": [
                "АрхитектураПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Wed, 14 Aug 2024 23:15:46 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "3",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "highlight\/highlight.js",
                    "highlight\/highlight.css"
                ],
                "og_images": []
            }
        },
        {
            "id": "2",
            "url": "https:\/\/dimasmotrov.ru\/all\/modulny-monolit-1\/",
            "title": "Модульный монолит (1)",
            "content_html": "<p>В моих целях на ближайшие полгода лучше разобраться с практиками и архитектурными подходами, которые применяются в моей текущей работе. Начать я захотел с такого понятия, как «Модульный монолит». Кому интересно аргументированно пообщаться — прошу под кат.<\/p>\n<blockquote>\n<p><i><b>Монолитная архитектура<\/b> — это традиционная универсальная модель проектирования ПО. Монолитный в данном контексте значит собранный в единое целое. Компоненты программы связаны и взаимозависимы, а не обладают слабой связанностью (_low coupling — >прим. перев._), как в случае модульных программ<\/i>.<\/p>\n<\/blockquote>\n<p>Описание выше — пример очень плохой ситуации, когда происходит «утечка БЛ»(или же сильное сплетение разных компонентов). Подобную систему поддерживать — очень дорого по трудозатратам и деньгам. Все наладом дышит, а капельницы для проекта заканчиваются быстрее, чем новые подвозят.<\/p>\n<p>Я уверен, что читатель в своих проектах с пеной у рта отказывается вливать конструкции с таким количеством связей в разные стороны, в том числе когда код «воюет не в ту сторону». Кроме случаев, когда дедлайн ближе, чем кажется(вы же берете себе задачу для улучшение подобного кусочка кода в ближайшее после хотфиксов время?).<\/p>\n<p>И тут у меня возникает вопрос: а зачем из каждого утюга орут, что монолит — это мерзость, которую всеми правдами и неправдами нужно избегать? Ребят, а вы зачем лезете в микросервисы, когда есть возможность добавить модульность в монолит, использовать между модулями четкие интерфейсы и в определенный момент(он сам к вами придет) вырезать нужные сервисы из достаточно опрятного монолита? Какое учение несет ваша церковь? Кто среди вас главный евангелист?<\/p>\n<p>Разберемся.<\/p>\n",
            "date_published": "2024-08-10T20:07:09+03:00",
            "date_modified": "2024-08-10T20:46:18+03:00",
            "tags": [
                "АрхитектураПО",
                "Разработка"
            ],
            "_date_published_rfc2822": "Sat, 10 Aug 2024 20:07:09 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "2",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        }
    ],
    "_e2_version": 4134,
    "_e2_ua_string": "Aegea 11.3 (v4134)"
}