<?xml version="1.0" encoding="UTF-8"?>

<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" >

  <channel>
    <title><![CDATA[Комментарии / Профиль jdev]]></title>
    <link>https://habr.com/ru/users/jdev/comments/</link>
    <description><![CDATA[Хабр: комментарии пользователя jdev]]></description>
    <language>ru</language>
    <managingEditor>editor@habr.com</managingEditor>
    <generator>habr.com</generator>
    <pubDate>Mon, 27 Apr 2026 07:57:40 GMT</pubDate>
    
    
      <image>
        <link>https://habr.com/ru/</link>
        <url>https://habrastorage.org/webt/ym/el/wk/ymelwk3zy1gawz4nkejl_-ammtc.png</url>
        <title>Хабр</title>
      </image>
    

    
      

      
        
  
    <item>
      <title>22.09.2025 15:14:23 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/949148/#comment_28868532</guid>
      <link>https://habr.com/ru/articles/949148/#comment_28868532</link>
      <description><![CDATA[<p>Нет, дихотомия не ложная. Судя по</p><blockquote><p>но <strong>мне</strong> хватает того факта, что если <strong>я</strong> написал тест на кейс Х, то он точно не стрельнет на проде. Значит <strong>меня</strong> не будут теребонькать на выходных.</p></blockquote><p>- скоуп разный:) В вашем случае "вся команда" - это лично вы в своей выделенной делянке, в которую никто кроме вас не лезет и из которой вы не вылазите и для которой работает "тесты зелёные - можно в прод":) Так тоже работает, если получается отгородиться от хаоса снаружи.</p><p>Но если в проекте нет владения кодом и любой разработчик может править любой код, то либо все пишут тесты, либо получить "<em>Это когда нажал кнопку и через короткое время, увидев зеленые тесты, точно знаешь, что все работает</em>" не получится.</p>]]></description>
      <pubDate>Mon, 22 Sep 2025 15:14:23 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>22.09.2025 07:06:18 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/949148/#comment_28866154</guid>
      <link>https://habr.com/ru/articles/949148/#comment_28866154</link>
      <description><![CDATA[<p>У людей из банковской тусовки часто встречаю "пром" - видимо от "Промышленная эксплуатация". В общем "устоянность" термина сильно зависит от тусовки:)</p>]]></description>
      <pubDate>Mon, 22 Sep 2025 07:06:18 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>22.09.2025 06:13:18 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/949148/#comment_28865880</guid>
      <link>https://habr.com/ru/articles/949148/#comment_28865880</link>
      <description><![CDATA[<blockquote><p>Просто начните писать.</p></blockquote><p>Не всё так просто. Начать писать -  необходимое, конечно, но недостаточное условие.</p><p><br>Чтобы получить</p><blockquote><p>Это когда нажал кнопку и через короткое время, увидев зеленые тесты, точно знаешь, что все работает.</p></blockquote><p>важно, чтобы вся команда писала тесты. И писала качественные тесты, а не отписки для лида, лишь бы отвалил.</p>]]></description>
      <pubDate>Mon, 22 Sep 2025 06:13:18 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.08.2025 06:56:29 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/934672/#comment_28672644</guid>
      <link>https://habr.com/ru/articles/934672/#comment_28672644</link>
      <description><![CDATA[<p>Абсолютная инкапсуляция чем-то принципиально отличается от Dependency Inversion Principle и Чистой архитектуры, о которых (в моём инфопузере) каждая собака знает? Не увидел этого в статье</p>]]></description>
      <pubDate>Thu, 07 Aug 2025 06:56:29 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>03.08.2025 04:23:50 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/933454/#comment_28654672</guid>
      <link>https://habr.com/ru/articles/933454/#comment_28654672</link>
      <description><![CDATA[<p>Я долго пытался есть этот кактус на Kotlin + Spring потому как теоретически мне идея нравится, но в итоге отказался.<br><br>Потому что:</p><ol><li><p>прокидывать ошибки по стеку руками - через чур гемморойно, имхо</p></li><li><p>Result плохо интегрируется с библиотеками. В частности Spring не откатит транзакцию, а корутины не закенсалят скоуп при возврате резалта. Транзакции точно, а коррутины скорее всего можно обработать напильником, но, опять же, через чур гемморойно, имхо</p></li><li><p>В Котлине нет нормальной возможности в верхнеуровневом методе вернуть пару разных ошибок из вызываемых методов и в итоге ошибка очень быстро превращается в Exception (корневой тип ошибок), чья информативность стремится к 0. Хотя это может решиться благодаря <a href="https://computas.com/blogg/exploring-rich-errors-in-kotlin-a-game-changer-from-kotlinconf-2025/" rel="noopener noreferrer nofollow">Rich errors</a> (<a href="https://2025.kotlinconf.com/talks/762779/" rel="noopener noreferrer nofollow">доклад</a> с их представлением).</p></li></ol><p>В итоге я разделил ошибки на восстановимые (которых в моих проектах очень мало) и не восстановимые. Восстановимые возвращаю резалтом и тут же обрабатываю, а не восстановимые бросаю исключениями которые улетают до контроллера (спецефичные для метода) или миддлваря (универсальные в духе resource-not-found). Всё это у меня подробно описано <a href="https://ergowiki.azhidkov.pro/docs/patterns/operations-impl/error-handling/" rel="noopener noreferrer nofollow">тут</a></p>]]></description>
      <pubDate>Sun, 03 Aug 2025 04:23:50 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.07.2025 06:55:23 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/925446/#comment_28538124</guid>
      <link>https://habr.com/ru/articles/925446/#comment_28538124</link>
      <description><![CDATA[<p>А, и собственно HTTP- запрос я заворачиваю в хелпер, поэтому если меняется только синтаксис, а не семантика (например параметр из пути в запрос уезжает), то это тест-кейсы не затрагивает.</p>]]></description>
      <pubDate>Mon, 07 Jul 2025 06:55:23 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.07.2025 06:48:39 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/925446/#comment_28538086</guid>
      <link>https://habr.com/ru/articles/925446/#comment_28538086</link>
      <description><![CDATA[<p>Это, наверное, зависит от контекста.</p><p>Я обычно перед тем как писать какой-то код, проектирую и согласовываю АПИ с фронтом. И тесты пишу через это АПИ. А если там будут существенные изменения, то модификация тестов - будет меньшей из проблем:)</p>]]></description>
      <pubDate>Mon, 07 Jul 2025 06:48:39 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.07.2025 06:29:47 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/925446/#comment_28538006</guid>
      <link>https://habr.com/ru/articles/925446/#comment_28538006</link>
      <description><![CDATA[<p>Я работаю через ТДД и там где есть возможность пытаюсь рефлексировать стоит ли оно того.</p><p>Пока набралось два кейса:</p><ol><li><p><a href="https://azhidkov.pro/posts/23/07/project-e-results/#_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%BE%D1%80%D0%B8%D0%B3%D0%B8%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8_%D0%B8_%D0%B5%D1%91_%D1%80%D0%B5%D0%B8%D0%BD%D0%B6%D0%B8%D0%BD%D0%B8%D1%80%D0%B8%D0%BD%D0%B3" rel="noopener noreferrer nofollow">Сравнение</a> трудозатрат на первоначальную разработку и её полный реинжиниринг</p></li><li><p>Субъективное ощущение продакта на разработку большой фичи (человеко-год) по ТДД в сравнении с другими фичами в том же проекте. Тут ретру пока не публиковал</p></li></ol><p>И в обоих кейсах разработка по ТДД была не медленнее разработки без тестов и команда допуска в 2-4 раза меньше багов.</p><p>Ну то есть с ТДД можно шипать фичи с той же скоростью, но при этом спокойно спать и в целом меньше стрессовать:)</p>]]></description>
      <pubDate>Mon, 07 Jul 2025 06:29:47 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.07.2025 05:16:53 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/925446/#comment_28537642</guid>
      <link>https://habr.com/ru/articles/925446/#comment_28537642</link>
      <description><![CDATA[<p>Спасибо, хорошая и нужная статья.</p><p>Только я бы добавил про</p><blockquote><p><strong>Изолируйте все внешние зависимости</strong></p><p>Ваша предметная область, ваша бизнес-логика, ваш основной модуль, причина, по которой вы пишете программное обеспечение, его самое сердце — должны быть защищены от внешнего мира.</p></blockquote><p>У меня последние лет 5 в куче разных проектов (среди прочих - автоматизация выделенного бизнес-процесса крупного ритейлера, медтех, автоматизация работы юридического департамента) 90% "причины по которой я пишу ПО" - положить данные в РСУБД. И поэтому 90% кода тестировать в изоляции от внешнего мира особого смысла нет. В общем, на мой взгляд, изоляция внешних зависимостей - не универсальное правило и нужна только в сложных предметных областях (я таких не видел, но предполагаю, что к ним относится банкинг, страхование, логистика, e-commerce).</p><p>А в остальных случаях  эффективнее по соотношению трудозатраты/(скорость + качество разработки), писать тесты как минимум с реальным PostgreSQL на RAM-диске.</p><p>И если заморочиться, такие тесты будут не критично медленнее - до 10 секунд на запуск 1 теста, и по 50мс в среднем на тест при запуске всего набора.</p><p>В остальном - всё плюсую.</p>]]></description>
      <pubDate>Mon, 07 Jul 2025 05:16:53 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>17.06.2025 08:06:24 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/919028/#comment_28445846</guid>
      <link>https://habr.com/ru/articles/919028/#comment_28445846</link>
      <description><![CDATA[<blockquote><p>Очень хорошая статья.</p></blockquote><p>Спасибо:)</p><blockquote><p>ак с точки зрения проверки типов работает, что вы в мапу списков помещаете не список, а единственное значение? Я вижу, что <code>groupedRow: Map&lt;</code><strong><code>String</code></strong><code>, List&lt;</code><strong><code>Any</code></strong><code>?&gt;&gt; </code>, но не понимаю, что такое делает + , что тип стал Map&lt;String, Any&gt;</p></blockquote><p><code>List&lt;Any?&gt;</code> - является подтипом <code>Any</code>.</p><p>"оператор" + - это на самом деле просто синтаксический сахар для создания копии мапы и добавления в неё элемента (копирование вместо добавления в имеющуюся - из-за ФП головного мозга)</p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/95b/c70/59c/95bc7059cb4600221e7fbabedb826aee.png" width="846" height="202"></figure><p>Соответственно:</p><ol><li><p>Метод <code>groupBy</code> возвращает <code>Table</code> aka <code>List&lt;Map&lt;String, Any&gt;&gt;</code></p></li><li><p>Лямбда, которая передаётся в <code>rowGroups.map</code> возвращает <code>Map&lt;String, List&lt;Any?&gt;&gt;</code></p></li><li><p>А сам метод map возвращает <code>List&lt;Map&lt;String, List&lt;Any?&gt;&gt;&gt;</code></p></li><li><p>И при возврате из метода <code>groupBy</code> выполняется upcast (приведение к супертипу) <code>List&lt;Map&lt;String, List&lt;Any?&gt;&gt;&gt;</code> -&gt; <code>List&lt;Map&lt;String, Any&gt;&gt;</code></p></li></ol>]]></description>
      <pubDate>Tue, 17 Jun 2025 08:06:24 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>28.03.2025 02:23:30 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/894766/#comment_28100570</guid>
      <link>https://habr.com/ru/articles/894766/#comment_28100570</link>
      <description><![CDATA[<p><a class="mention" href="/users/razon">@razon</a></p><p>И как вы находите границы микросервисов и ограниченных контекстов?</p><p>Условно ко мне пришёл продакт, говорит хочу фичу логистики, она должна делать то-то и то-то - в результате какого процесса у меня появятся артефакты/термины "ПВЗ Customer", "ПВЗ Маппер" и т.д. и "Ограниченный контекст ПВЗ", "Ограниченный контекст Геоданные" и т.д.</p>]]></description>
      <pubDate>Fri, 28 Mar 2025 02:23:30 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>28.03.2025 02:17:15 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/894766/#comment_28100562</guid>
      <link>https://habr.com/ru/articles/894766/#comment_28100562</link>
      <description><![CDATA[<p><a href="https://habr.com/ru/users/razon/" rel="noopener noreferrer nofollow"> </a><a class="mention" href="/users/razon">@razon</a></p><p>Поясните, пожалуйста - куда и на каком уровне абстракции фронт встраивается в вашей модели?</p><p>В моей практике фронтовые приложения/компоненты зачастую работают со множеством (всеми) слабосвязанных между собой компонентов бэка и, кажется, сломают всю стройную картину - если фронт засунуть в один из ограниченных контекстов - он его coupling убьёт, а если в отдельный - у него самого ещё хуже coupling будет.</p>]]></description>
      <pubDate>Fri, 28 Mar 2025 02:17:15 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>10.03.2025 09:33:23 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/888428/#comment_28021066</guid>
      <link>https://habr.com/ru/articles/888428/#comment_28021066</link>
      <description><![CDATA[<p>Так ни я в <strong>примечании</strong> к этому посте, ни Хориков в посте, на который я ссылаюсь, не пишем, что от моков надо <strong>полностью</strong> отказаться.</p><p>Я пишу, что отношение тестов без моков к тестам с моками может быть 100 к 1. То есть без моков <strong>никак </strong>- бывает в одном случае из ста. В моей практике последних 4 лет - 9 коммерческих проектов суммарно на ~100К строк Котлин-кода.</p><p>При этом среднее время теста по моим проектам - порядка 50мс:</p><figure class=""><img src="https://habrastorage.org/getpro/habr/upload_files/0ef/ea9/d7a/0efea9d7af5216dcaaced9907071fb4c.png" alt="Скрин тестов публичного Trainer Advisor-а" title="Скрин тестов публичного Trainer Advisor-а" width="369" height="41"><div><figcaption>Скрин тестов публичного <a href="https://github.com/ergonomic-code/Trainer-Advisor" rel="noopener noreferrer nofollow">Trainer Advisor</a>-а</figcaption></div></figure><p>Тут ещё, пожалуй, стоит пояснить, что под "моками" я понимаю именно мокирование собственных типов с помощью условного Mockito. А против условных WireMock, GreenMail и т.п. я ничего не имею, включая верификацию вызовов - это часть наблюдаемого (снаружи) повдениия системы, которая должна быть специфицирована в тестах.</p><p></p>]]></description>
      <pubDate>Mon, 10 Mar 2025 09:33:23 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>09.03.2025 14:49:13 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/888428/#comment_28018672</guid>
      <link>https://habr.com/ru/articles/888428/#comment_28018672</link>
      <description><![CDATA[<p>Добавлю ещё, что эти тезисы подтверждаются фактами из моей практики</p><p>В моём предыдущем проекте с тестами без моков за 500 человеко-часов, которые я рассматривал в ретроспективе было 0.5 бага на задачу или 0.05 бага/человеко-час (<a href="https://azhidkov.pro/posts/23/07/project-e-results/#_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%BD%D0%BE%D0%B2%D1%8B%D1%85_%D1%84%D0%B8%D1%87_%D0%B8_%D1%82%D0%B5%D0%BA%D1%83%D1%87%D0%BA%D0%B0" rel="noopener noreferrer nofollow">подробности</a>).</p><p>В текущем проекте за примерно человеко/год разработки нашли 2 бага (не соответствие поведения софта требованиям) и 0 регрессий. Проект ещё не завершён, поэтому ретру не проводил и точных цифр пока нет.</p><p>В этом же проекте с 93% покрытием кода тестами без моков я недавно сделал пару серьёзных изменений в модели и рефакторингов: сделал один из агрегатов частью другого, ввёл разделение доменной модели и модели персистанса, добавил версионирование частей агрегата (условно таблички и её строк) - в ходе этой работы не было внесено <strong>ни одной регрессии</strong>.</p><p>А рефакторинг (20 затронутых файлов файлов, 482 добавленных строки, 172 удалённых строки) не содержал <strong>ни одной строки изменений</strong> в тестах.<br>Это красноречивая иллюстрация того, что хорошие тесты проверяют поведение системы, а не её реализацию и, как следствие, не меняются при рефакторинге - изменении структуры кода, без изменения его поведения.</p>]]></description>
      <pubDate>Sun, 09 Mar 2025 14:49:13 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>09.03.2025 09:37:37 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/888428/#comment_28017874</guid>
      <link>https://habr.com/ru/articles/888428/#comment_28017874</link>
      <description><![CDATA[<p>Я удивлён, что коммент выше заминусили в свете того, что все классики TDD предостерегают от повсеместного использования моков.</p><p>Оставлю здесь мудрость древних для будущих поколений:</p><blockquote><p>My personal practices…​ <strong>I’m mock almost nothing.</strong> If I can’t figure out how to test efficiently with the real stuff I find another way of creating a feedback loop for myself…​ I just don’t go very far down the mock path. I look at a code where you have mocks returning mocks returning mocks and…​ my experience is if I have…​ If I use TDD I can refactor stuff and then I heard these stories people say well I use TDD and now I can’t refactor anything and I feel like I couldn’t understand that and then I started looking at their tests…​ <strong>Well if you have mocks returning mocks returning mocks your test is completely coupled to the implementation…​</strong> <strong>not on the interface but the exact implementation of some object …​</strong> three streets away …​ of course you can’t change anything without breaking a test. So that for me is too high a price to pay that’s not not a trade-off I’m willing to make.</p><p>---</p><p>Мои личные практики…​ <strong>Я почти ничего не мокаю.</strong> Если я не могу понять, как эффективно тестировать c реальными зависимостями, я нахожу другой способ создать для себя цикл обратной связи…​ Я просто не хожу далеко по пути мокирования. Я смотрю на код, в котором у вас есть моки, возвращающие моки, возвращающие моки, и…​ мой опыт показывает, что если у меня есть…​ Если я использую TDD, я могу рефакторить кода, а потом я слышу эти истории, когда люди говорили, что я использую TDD, а теперь я ничего не могу отрефакторить, и я не мог этого понять, пока не начал начал смотреть на их тесты…​ <strong>Ну, если у вас моки возвращают моки возвращающие моки, то ваш тест полностью связан с реализацией…​</strong> <strong>не с интерфейсом, а с точной реализацией какого-либо объекта…​</strong> в трех кварталах отсюда…​ конечно, вы ничего не сможете изменить, не сломав тест. Так что для меня это слишком высокая цена, на которую я не готов пойти ни в коем случае.</p><p> — Кент Бек, основатель движения TDD, автор JUnit<br><a href="https://youtu.be/z9quxZsLcfo?t=1270" rel="noopener noreferrer nofollow">TW Hangouts | Is TDD dead?</a> </p></blockquote><blockquote><p>Personally I’ve always been a old fashioned classic TDDer and thus far I don’t see any reason to change. <strong>I don’t see any compelling benefits for mockist TDD, and am concerned about the consequences of coupling tests to implementation</strong>.</p><p>This has particularly struck me when I’ve observed a mockist programmer. I really like the fact that while writing the test you focus on the result of the behavior, not how it’s done. A mockist is constantly thinking about how the SUT is going to be implemented in order to write the expectations. This feels really unnatural to me.</p><p>---</p><p>Лично я всегда был сторонником старомодного классического подхода и до сих пор не вижу причин меняться. <strong>Я не вижу никаких убедительных преимуществ для mockist TDD и обеспокоен последствиями привязки тестов к реализации.</strong></p><p>Это особенно поразило меня, когда я наблюдал за программистом-мокистом. Мне очень нравится, что при написании теста вы сосредотачиваетесь на результате поведения, а не на том, как оно достигается. Мокист постоянно думает о том, как будет реализован SUT, чтобы описать ожидания. не это кажется действительно неестественным.</p><p> — Мартин Фаулер, автор таких книг как "Рефакторинг" и "Шаблоны корпоративных приложений"<br><a href="https://martinfowler.com/articles/mocksArentStubs.html" rel="noopener noreferrer nofollow">Mocks Aren’t Stubs</a> </p></blockquote><blockquote><p>In short, however, <strong>I recommend that you mock sparingly</strong>. Find a way to test – design a way to test – your code so that it doesn’t require a mock. Reserve mocking for architecturally significant boundaries; and then be ruthless about it. Those are the important boundaries of your system and they need to be managed, not just for tests, but for everything.</p><p><strong>And write your own mocks. Try to depend on mocking tools as little as possible. Or, if you decide to use a mocking tool, use it with a very light touch.</strong></p><p>If you follow these heuristics you’ll find that your test suites run faster, <strong>are less fragile</strong>, have higher coverage, and that the designs of your applications are better.</p><p>---</p><p>Вкратце, <strong>я рекомендую вам использовать моки с осторожностью</strong>. Найдите способ тестирования – спроектируйте способ тестирования вашего кода, чтобы он не требовал моков. Оставьте моки для архитектурно значимых границ, и там безжалостно мокайте всё (дословный перевод: "и затем будьте безжалостны к нему"). Это важные границы вашей системы, и ими нужно управлять не только для тестов, но и для всего остального.</p><p><strong>И пишите свои собственные моки. Старайтесь как можно меньше зависеть от инструментов мокирования. Или, если вы решите использовать инструмент для создания макета, используйте его минимально (дословный перевод: "очень лёгкими касаниями").</strong></p><p>Если вы будете следовать этим эвристическим рекомендациям, то обнаружите, что ваши наборы тестов выполняются быстрее, <strong>они менее хрупкие</strong>, имеют более широкое покрытие и что дизайн ваших приложений лучше.</p><p> — Роберт Мартин, автор SOLID и Чистой архитектуры<br><a href="https://blog.cleancoder.com/uncle-bob/2014/05/10/WhenToMock.html" rel="noopener noreferrer nofollow">When To Mock</a> </p></blockquote><p></p>]]></description>
      <pubDate>Sun, 09 Mar 2025 09:37:37 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.03.2025 18:02:58 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/888428/#comment_28012888</guid>
      <link>https://habr.com/ru/articles/888428/#comment_28012888</link>
      <description><![CDATA[<p>Я пожалуй даже код левого блока оркистрации скину:)</p><pre><code class="kotlin">private fun updateTable(updateTableRq: UpdateTableRq): Table? =
        transactionTemplate.execute {
            if (updateTableRq.correctionData.isNullOrEmpty()) {
                return@execute null
            }

            val correctedRows = updateTableRq.correctedRows()
            val rowsExternalIds = correctedRows.map { it.externalId }.toSet()
            val table = tablesService.findTableByExternalIds(rowsExternalIds)

            if (table == null) {
                return@execute null
            }

            if (correctedRows.size != table.rows.size) {
                throw TableRowNotFoundException()
            }

            val settings =  tablesSettingsRepo.findByDocumentId(table.outgoingDocument.id)
                                      ?: throw ResourceNotFoundException()

            val referenceExternalIds = correctedRows.collectReferenceFieldsValues(settings.fields)
            val referenceIds = referencesService.findReferenceMapByExternalIds(referenceExternalIds)

            val systemName = table.rows.first().externalId.systemName
            val parsedCorrections = correctedRows.associateBy(UpdatedRowRq::externalId) {
                it.parseFieldValues(systemName, settings.apiFieldsById, referenceIds)
            }

            tablesService.updateTable(table) {
                val rowCorrections = parsedCorrections[it.externalId.externalId]
                tableRowsFactory.updateRow(settings, it, rowCorrections)
            }
        }</code></pre><p> - кажется, он очень похож на ваш пример кода и, похож на ваш пример с созданием версии - тут как раз создаётся новая версия некой (хитрой по сути и динамически настраиваемой по структуре) таблицы.</p><p></p>]]></description>
      <pubDate>Fri, 07 Mar 2025 18:02:58 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.03.2025 17:45:55 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/888428/#comment_28012828</guid>
      <link>https://habr.com/ru/articles/888428/#comment_28012828</link>
      <description><![CDATA[<p>Ещё так попробую проиллюстрировать свою точку зрения.</p><p>Разделением ИО и логики я борюсь с этим:</p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/c82/054/e6d/c82054e6d08e771bc47d158eca092a76.png" width="2759" height="959"></figure><p>Легенда:</p><ul><li><p>Синие блоки - оркестрация, блоки которые взывают более одного типа других блоков и имеют <a href="https://www.sonarsource.com/docs/CognitiveComplexity.pdf" rel="noopener noreferrer nofollow">когнитивную сложность </a>&lt;= 4</p></li><li><p>Красные блоки - ввод, блоки которые делают ввод и имеют когнитивную сложность &lt;= 4</p></li><li><p>Зелёные блоки - бизнес-логика, блоки, которые не вызывают ввод или вывод и имеют когнитивную сложность &lt;= 15</p></li><li><p>Жёлтые блоки - вывод, блоки, которые делают вывод и имеют когнитивную сложность &lt;= 4</p></li><li><p>Оранжевые блоки - месиво, блоки которые и делают ио (их сложно тестить и сложно понимать из-за необходимости учитывать перформанс, обработку ошибок и порядок выполнения) и имеют когнитивную сложность &gt; 4 (их сложно читать и хочется тестить)</p></li></ul><p>Или вот ещё не раскрашенный пример:<br></p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/3e6/f76/bb8/3e6f76bb836cc71101c337c5a366f080.png" width="1280" height="673"></figure><p>И в идеале я стремлюсь (и очень часто у меня получается) к такому:</p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/7bf/52f/be8/7bf52fbe86edb25da1b17b783c90f50e.png" width="2033" height="844"></figure><p>Но вы правы, что не всегда всё так просто и красиво. Однако достичь вот такого - вполне реально и в "волосатых" случаях:</p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/ea2/dad/3c6/ea2dad3c64b75a02c4a9cc0965309691.png" width="8346" height="3043"></figure><p>Эта картинка как раз ближе к IOSP/IODA, чем к ФА. Ну и да, оранжевый блок я подчищу в понедельник - этот кусок только-только дописали просрав дедлайн из-за недооценки "волосатости" задачи:)</p>]]></description>
      <pubDate>Fri, 07 Mar 2025 17:45:55 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.03.2025 11:55:12 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/888428/#comment_28011502</guid>
      <link>https://habr.com/ru/articles/888428/#comment_28011502</link>
      <description><![CDATA[<p>Разница в сложности задачи.</p><p>В вашем примере я не вижу сложной бизнес-логики, которую стоит выделять.</p><p>А если вы приведёте пример с действительно сложной в моём пониманиии бизнес-логикой - мне скорее всего будет а) лень б) не хватать вводных, чтобы отрефакторить его и разделить логику и ио:)</p>]]></description>
      <pubDate>Fri, 07 Mar 2025 11:55:12 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.03.2025 11:47:10 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/888428/#comment_28011466</guid>
      <link>https://habr.com/ru/articles/888428/#comment_28011466</link>
      <description><![CDATA[<p>ПФА - это частный случай того, что вы называете многослойной архитектуры (как и чистая, и просто функциональная), который накладывает доп. ограничение в виде неизменямых данных.</p><p>Но! В моём окружающем мире люди называют многослойной архитектуру, в которой domain logic зависит от persistence layer (который может выглядеть интерфейсами Spring Data репозиториев). И от такой вариации многослойной архитектуры, ПФА отличается ещё и запретом на обращение к persistence layer из domain logic.</p><p>На самом деле, видимо, тут же кроется и ответ на ваш <a href="https://habr.com/ru/articles/888428/comments/#comment_28007500" rel="noopener noreferrer nofollow">первый комментарий</a>: если делать многослойную архитектуру так, как её делают вокруг меня - она ни от чего не спасает и проекты по ней быстро превращается в большой ком грязи. Тот же <a href="https://azhidkov.pro/posts/24/11/structured-design/#_%D0%BA%D0%B5%D0%B9%D1%81_2_project_daniel" rel="noopener noreferrer nofollow">Project Daniel</a> - там были даже отдельные Maven-модули application-services и domain-services. Но это не спасло проект от превращения в большой ком грязи.</p><p>А ваша многослойная архитектура с правилом "доменная логика, которая не связана с вводом-выводом" как таки и позволяет "уйти от жёсткости и хрупкости".</p>]]></description>
      <pubDate>Fri, 07 Mar 2025 11:47:10 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

  
    <item>
      <title>07.03.2025 10:39:57 </title>
      <guid isPermaLink="true">https://habr.com/ru/articles/888428/#comment_28011098</guid>
      <link>https://habr.com/ru/articles/888428/#comment_28011098</link>
      <description><![CDATA[<blockquote><p>Верхний уровень логики (application logic) занимается оркестрацией. Он обращается как к доменной логике, которая не связана с вводом-выводом, так и к другой группе функционала, которая связана только с вводом-выводом.</p></blockquote><p>Добавьте туда модель данных в виде неизменямых классов и это ровно то, что я называю "Промышленной функциональной архитектурой".</p><p>Почему я считаю, что незименямость важна я писал <a href="https://azhidkov.pro/posts/23/10/why-fp/#_%D0%B2%D0%B5%D1%80%D0%BE%D1%8F%D1%82%D0%BD%D0%B0%D1%8F_%D0%BF%D1%80%D0%B8%D1%87%D0%B8%D0%BD%D0%B0_%D0%BD%D0%B8%D0%B7%D0%BA%D0%BE%D0%B9_%D1%81%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8_%D1%84%D0%BF_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC" rel="noopener noreferrer nofollow">тут</a>. А реальный кейс (но без кода, к сожалению) как "конфетка превращается в шпинат" - <a href="https://t.me/ergonomic_code/483" rel="noopener noreferrer nofollow">здесь</a></p><p></p>]]></description>
      <pubDate>Fri, 07 Mar 2025 10:39:57 GMT</pubDate>
      <dc:creator><![CDATA[]]></dc:creator>
    </item>
  

      

      

    
  </channel>
</rss>
