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>