HTML Diff
1 added 1 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>На протяжении всего курса, в практической части, мы разрабатывали сценарии, в которых происходило только добавление сущностей. Добавление это наиболее простая операция из всех возможных, потому что в ней отсутствует (почти) изменяемость данных. Но большинство сценариев в реальной жизни обычно связано с изменением:</p>
1 <p>На протяжении всего курса, в практической части, мы разрабатывали сценарии, в которых происходило только добавление сущностей. Добавление это наиболее простая операция из всех возможных, потому что в ней отсутствует (почти) изменяемость данных. Но большинство сценариев в реальной жизни обычно связано с изменением:</p>
2 <ul><li>Обновление расписания</li>
2 <ul><li>Обновление расписания</li>
3 <li>Возврат билета</li>
3 <li>Возврат билета</li>
4 <li>Удаление кинозала</li>
4 <li>Удаление кинозала</li>
5 <li>Изменение конфигурации кинозала</li>
5 <li>Изменение конфигурации кинозала</li>
6 </ul><p>И вот тут начинаются настоящие проблемы. Попробуйте ответить себе на следующие вопросы:</p>
6 </ul><p>И вот тут начинаются настоящие проблемы. Попробуйте ответить себе на следующие вопросы:</p>
7 <ul><li>Можно ли безопасно удалить из системы кинозал?</li>
7 <ul><li>Можно ли безопасно удалить из системы кинозал?</li>
8 <li>Можно ли безопасно изменить конфигурацию зала в любой момент времени?</li>
8 <li>Можно ли безопасно изменить конфигурацию зала в любой момент времени?</li>
9 <li>Простая ли операция возврата билета?</li>
9 <li>Простая ли операция возврата билета?</li>
10 </ul><p>На все эти (и многие другие) вопросы ответ: нет. Возврат билета затрагивает множество связанных сущностей. Например, в зале нужно освободить место для возможности перепродажи. Удаление кинозала из системы приведёт к тому, что все предыдущие просмотры фильмов в этом зале станут не консистентными, мы потеряем историю операций. Изменение конфигурации зала приведёт к поломке старых покупок. Удаление сеанса связано с возвратом билетов, а эта операция, в свою очередь, порождает цепочку других изменений.</p>
10 </ul><p>На все эти (и многие другие) вопросы ответ: нет. Возврат билета затрагивает множество связанных сущностей. Например, в зале нужно освободить место для возможности перепродажи. Удаление кинозала из системы приведёт к тому, что все предыдущие просмотры фильмов в этом зале станут не консистентными, мы потеряем историю операций. Изменение конфигурации зала приведёт к поломке старых покупок. Удаление сеанса связано с возвратом билетов, а эта операция, в свою очередь, порождает цепочку других изменений.</p>
11 <p>Итак, чем же нам грозит изменяемость:</p>
11 <p>Итак, чем же нам грозит изменяемость:</p>
12 <ul><li>Сложное обновление связанных сущностей;</li>
12 <ul><li>Сложное обновление связанных сущностей;</li>
13 <li>Отсутствие истории;</li>
13 <li>Отсутствие истории;</li>
14 <li>Рассинхронизация;</li>
14 <li>Рассинхронизация;</li>
15 <li>Повреждение старых связей.</li>
15 <li>Повреждение старых связей.</li>
16 </ul><p>В связи с этим хочется сформулировать первое правило изменяемости:</p>
16 </ul><p>В связи с этим хочется сформулировать первое правило изменяемости:</p>
17 <blockquote><p>Не изменяй!</p>
17 <blockquote><p>Не изменяй!</p>
18 - </blockquote><p>И действительно, на практике часто можно и нужно проектировать систему так, чтобы изменяющие действия превращались в append only. Проницательный читатель вероятно заметил, что та идея очень сильно коррелирует с главным принципом функционального программирования, а именно отсутствием изменений. Это действительно так, проблема изменяемого состояния - это не проблема кода и программирования, это особенность физической реальности. И на практике гораздо лучше пытаться уйти от изменений, чем пытаться создать систему, которая консистентно обновляет все нужные связи, что в общем случае невозможно и рождает очень много случайной сложности.</p>
18 + </blockquote><p>И действительно, на практике часто можно и нужно проектировать систему так, чтобы изменяющие действия превращались в append only. Проницательный читатель вероятно заметил, что эта идея очень сильно коррелирует с главным принципом функционального программирования, а именно отсутствием изменений. Это действительно так, проблема изменяемого состояния - это не проблема кода и программирования, это особенность физической реальности. И на практике гораздо лучше пытаться уйти от изменений, чем пытаться создать систему, которая консистентно обновляет все нужные связи, что в общем случае невозможно и рождает очень много случайной сложности.</p>
19 <p>Разберём несколько простых примеров.</p>
19 <p>Разберём несколько простых примеров.</p>
20 <h2>Изменение конфигурации зала</h2>
20 <h2>Изменение конфигурации зала</h2>
21 <p>В такой ситуации правильно создать новый зал с другой конфигурацией, а предыдущий архивировать. То есть перевести конечный автомат жизненного цикла зала в состояние archived. Соответственно, все старые данные остаются в согласованном виде. У нас появляется история, и мы можем отследить момент, в который произошла физическая перепланировка зала.</p>
21 <p>В такой ситуации правильно создать новый зал с другой конфигурацией, а предыдущий архивировать. То есть перевести конечный автомат жизненного цикла зала в состояние archived. Соответственно, все старые данные остаются в согласованном виде. У нас появляется история, и мы можем отследить момент, в который произошла физическая перепланировка зала.</p>
22 <h2>Возврат билетов</h2>
22 <h2>Возврат билетов</h2>
23 <p>При возврате билета, правильно не удалять записи о том, что был приход денег. Правильно создать новую запись, в которой отражается расход. Кроме стандартных плюсов, мы получаем возможность проводить глубокий бизнес-анализ ситуации с возвратами. Сам билет также может быть переведён в состояние (везде конечные автоматы) возвращён.</p>
23 <p>При возврате билета, правильно не удалять записи о том, что был приход денег. Правильно создать новую запись, в которой отражается расход. Кроме стандартных плюсов, мы получаем возможность проводить глубокий бизнес-анализ ситуации с возвратами. Сам билет также может быть переведён в состояние (везде конечные автоматы) возвращён.</p>
24 <h2>Current</h2>
24 <h2>Current</h2>
25 <p>Ещё один подход для ухода от изменений связан с тем, что вводится понятие current. Например, на Хекслете есть понятие "Упражнение". Это задание, которое выполняется в нашей ide. Перед тем как новая версия упражнения попадает на сайт, она проходит этап сборки и верификации. На этом этапе происходит упаковка всех зависимостей и кода упражнения в Docker-образ, а также выполнение различных проверок на работоспособность, в первую очередь, конечно же, тестов.</p>
25 <p>Ещё один подход для ухода от изменений связан с тем, что вводится понятие current. Например, на Хекслете есть понятие "Упражнение". Это задание, которое выполняется в нашей ide. Перед тем как новая версия упражнения попадает на сайт, она проходит этап сборки и верификации. На этом этапе происходит упаковка всех зависимостей и кода упражнения в Docker-образ, а также выполнение различных проверок на работоспособность, в первую очередь, конечно же, тестов.</p>
26 <p>Исправлять упражнение ни в коем случае нельзя. Потому что всегда есть пользователи, которые проходят старую версию упражнения. И если бы мы делали изменение текущей версии, то у части пользователей упражнение перестало бы работать. Выход из ситуации очень простой. Мы разделяем понятия Exercise и Exercise::Build. И после того, как успешно пройдёт новый Exercise::Build, в Exercise будет записан Current Exercise Build. После этого все новые старты будут использовать эту сборку, а старые - ту, которая была актуальна на момент старта. Для функционирования такого механизма нужно всегда записывать Current Exercise Build в объект, представляющий собой запущенную практику, тогда всё будет работать даже если появится новая сборка.</p>
26 <p>Исправлять упражнение ни в коем случае нельзя. Потому что всегда есть пользователи, которые проходят старую версию упражнения. И если бы мы делали изменение текущей версии, то у части пользователей упражнение перестало бы работать. Выход из ситуации очень простой. Мы разделяем понятия Exercise и Exercise::Build. И после того, как успешно пройдёт новый Exercise::Build, в Exercise будет записан Current Exercise Build. После этого все новые старты будут использовать эту сборку, а старые - ту, которая была актуальна на момент старта. Для функционирования такого механизма нужно всегда записывать Current Exercise Build в объект, представляющий собой запущенную практику, тогда всё будет работать даже если появится новая сборка.</p>
27 <p>На практике это очень простая тактика. Хекслет использует её просто повсеместно, и, таким образом, мы сильно снижаем затраты на синхронизацию данных.</p>
27 <p>На практике это очень простая тактика. Хекслет использует её просто повсеместно, и, таким образом, мы сильно снижаем затраты на синхронизацию данных.</p>