раздел 27
Gleam на практике
Gleam, минималистичный типизированный язык на BEAM. Первая половина, от первого gleam new до Wisp, Lustre и Telegram-бота: ADT, pattern matching, парсинг через Decoder, OTP-акторы, и тот же код в Erlang и в JavaScript. Вторая половина, серия по функциональному DDD: Value Object, Aggregate, паттерн Decider, event sourcing, CQRS и saga.
Раздел открыт для всех. Войди, чтобы отмечать пройденные уроки, вести заметки и решать задачи в редакторе. войти
Уроки раздела
- Введение в Gleam, язык на BEAM
- Начало работы: gleam new, базовые типы, первый проект
- Функции, пайплайны и замыкания
- Типы данных и коллекции: ADT, pattern matching, List, Dict
- Рекурсия, свёртки и обработка ошибок
- Строки, BitArray и стандартная библиотека
- Type Safety и Parse Don't Validate
- Erlang FFI и системное программирование
- JavaScript FFI и фронтенд интеграция
- Процессы и OTP: акторы, supervisor, let it crash
- Тестирование на Gleam: gleeunit, property-based, snapshot
- Веб-разработка с Wisp
- Фронтенд с Lustre: TEA-архитектура в браузере
- Заключение и следующие шаги
- Telegram-бот с Telega 3: роутер, диалоги, flow, эксплуатация
- Functional DDD на Gleam: с чего начинаем
- Value Object и Email: opaque + smart-конструктор
- Money: валюта как phantom-параметр
- Entity и Customer: identity против equality
- Aggregate Reservation: стек инвариантов через result.try
- Domain Events: команды и события как ADT
- Repository как порт: in-memory backend для тестов
- Bounded Context: один Customer, два несовместимых типа
- Composition root на Wisp: где собирается граф зависимостей
- SQLite-репозиторий через sqlight: реальный adapter под тот же порт
- Конфиг и build_repo: финальная сборка первого блока
- Event Modeling: от свимлейна на доске к типам Gleam
- Decider Жереми Шассена: три функции вместо агрегата
- Event Sourcing: хранить факты, а не состояние
- CQRS: разделить запись и чтение, проекция как свёртка
- Process Manager и Saga: charge-on-checkout с компенсацией
- Aggregateless ES: единый стрим и Facts вместо агрегата
- Кейс Decider: Uno как FSM с ходом и направлением
- Континуации: одна бизнес-логика, любые эффекты
Темы и кластеры
Старт
- Введение, Gleam и BEAMЗачем ещё один ФП-язык, что такое BEAM, чем Gleam отличается от Elixir, Haskell и Elm. Двойной таргет: Erlang и JavaScript.есть урок
- Функции, пайплайны и замыканияPipeline `|>`, замыкания, именованные (labelled) аргументы, частичное применение. Это слой, где Gleam ощущается как чистый язык функций.есть урок
- gleam new и базовые типыУстановка, структура проекта, базовые типы: Int, Float, String, Bool. Особенности арифметики Float (точка-операторы).есть урок
Типы и данные
- Рекурсия, свёртки, Result и OptionРекурсия с хвостовой оптимизацией, fold как обобщение, ошибки как значения: Result, Option, use + result.try, Railway-Oriented Programming.есть урок
- Строки, BitArray и стандартная библиотекаСтроки как UTF-8, BitArray для битовых протоколов, обзор stdlib: string, list, dict, io, int, float.есть урок
- ADT, pattern matching, List и DictАлгебраические типы данных, custom types, pattern matching через case, кортежи, records, List, Dict. Невозможные состояния невыразимы.есть урок
Дисциплина типов
BEAM и FFI
- JavaScript FFI и target = javascriptТот же Gleam-код собирается в JS-модуль. Подключение JS через FFI, обёртка fetch, минимальные хелперы DOM.есть урок
- Erlang FFI: @external, atoms, ETSВызов Erlang-функций через @external, работа с atoms, ETS-таблицы как in-memory KV-стор, низкоуровневое spawn/send/self.есть урок
- OTP, акторы, supervisor, let it crashgleam/otp/actor как типобезопасная обёртка над BEAM-процессами. Subject и Selector. Запрос-ответ через actor.call. Supervisor и три стратегии перезапуска.есть урок
Практика
- Куда дальше: экосистема BEAM и HexРаспределённые системы на BEAM, Phoenix-связка через Elixir, Hex-пакеты, профилирование через :observer.есть урок
- Тестирование: gleeunit, qcheck, birdiegleeunit для юнитов, qcheck для property-based проверок, birdie для snapshot. Те же идеи, что в RTL + Vitest, разный сахар.есть урок
- Веб на Wisp: запрос-ответ, middleware, JSONWisp, веб-фреймворк на функциях. Роутер как case, middleware как обёртки, JSON через gleam_json, http-сервер на Mist внутри supervisor-дерева.есть урок
- Lustre: TEA-архитектура для браузераThe Elm Architecture в Gleam: model, msg, update, чистый view. Никаких хуков, side-effects через Effect и Subscription.есть урок
- Telegram-бот на Telega: команды, session, flowФинальный проект серии. Команды, роутер, middleware, Session, Conversation API (wait_*), Flow API как конечные автоматы с историей.есть урок
Functional DDD
- Кейс Decider: Uno как FSM с историейПараллельный мини-домен. Тот же Decider, что в 28, но карты, ход, направление, штрафы. BDD-сценарии по `gleamuno` и `Papipo/uno`.есть урок
- Конфиг и build_repo: финальная сборкаКат 10. envoy для env-переменных, фабрика `build_repo(config)` возвращает нужный adapter. На этом первая половина серии закрывается.есть урок
- Entity: Customer, идентичность против равенстваКат 3. Customer как entity с устойчивым `CustomerId`. Два клиента равны по id, не по полям. `update_email` возвращает новую запись.есть урок
- Event Modeling: воркшоп от свимлейна к кодуАдам Дымитрук. Команды (синие), события (оранжевые), read-models (зелёные), UI (жёлтые). Свимлейн на доске, потом перенос в типы.есть урок
- Functional DDD на Gleam: с чего начинаемЗачем серия по функциональному DDD, что заберём через 18 уроков, какой сквозной домен (Hotel Booking + Uno), три тезиса North Star.есть урок
- Money: валюта как phantom-параметрКат 2. Money(currency) с phantom-параметром, сложение возможно только в одной валюте, конвертация через FX-rate, `use <-` как guard.есть урок
- Value Object и Email через opaque + smart constructorКат 1 escherize. Email как opaque-тип, конструктор только через `Email.parse`, валидация на границе, дальше система работает с типом.есть урок
- Aggregate: Reservation, FSM и стек инвариантовКат 4. Reservation это агрегат: ADT состояний (NotPlaced, Reserved, CheckedIn, CheckedOut, Cancelled). Переходы возвращают Result, невозможные состояния не выражаются в типе.есть урок
- Bounded Context: разделение на модули и словарьКат 7. Один и тот же `Customer` в Reservations это не то же, что `Customer` в Billing. Каждый context, свой модуль, свой словарь.есть урок
- Domain Events: команды и события ReservationКат 5. Команда и событие это два разных типа. `decide(state, command) -> Result(List(Event), Error)`. Event это факт Reservation, который уже произошёл.есть урок
- Repository как порт: OrderRepo и in-memory backendКат 6. Repository это тип-запись с функциями `load` и `save`, домен не знает про базу. Сначала in-memory реализация на dict для тестов.есть урок
- Composition root на Wisp: где собирается граф зависимостейКат 8. Wisp-приложение принимает Repo как параметр, корень собирает реальные функции в одном месте. HTTP-роутер тонкий, домен чистый.есть урок
- CQRS: разделение write- и read-сторонКоманды идут через Decider, запросы, через отдельные read-models. Projection подписывается на стрим и материализует state для UI.есть урок
- Decider Жереми Шассена: универсальная единица домена`Decider(command, state, event, error)` плюс `decide`, `evolve`, `initial_state`. Из трёх функций собирается агрегат, FSM, процесс-менеджер.есть урок
- Event Sourcing: стрим событий, replay, снапшотыСостояние не таблица, а свёртка событий. `fold(events, initial_state, evolve)`. Snapshot, ускорение replay. Optimistic concurrency через expected_version.есть урок
- Process Manager и Saga: charge-on-checkoutLong-running сценарий через границу контекстов: Front Desk checks out, Billing получает событие, выставляет счёт, ошибка, компенсация.есть урок
- SQLite-репозиторий через sqlight: реальный adapterКат 9. Schema, миграции, encode/decode для Order, транзакции. Repo меняется на SQLite, домен и тесты не меняются ни на строчку.есть урок
- Aggregateless ES: Facts вместо агрегатовgleavent_sourced, единый стрим, Fact-функции, command-handlers без агрегатных границ. Когда транзакционная граница искажает модель.есть урок