раздел 25
Effect-TS
Архивный курс, больше не обновляется: актуальный Effect живёт в «Effect: Тихая Академия» (46). Effect-TS от Effect<A,E,R> и Schema до Stream, STM, batching, terminal. Сквозной проект Pulse, мониторинг URL: один Layer-граф, две поверхности (CLI на @effect/cli и HTTP-сервер на @effect/platform-node), общий runtime. Дальше блок Functional DDD на Effect и продовые уроки.
Начать раздел
Хочу пройти
Не получилось изменить список. Попробуй ещё раз.
Раздел открыт для всех. Войди, чтобы отмечать пройденные уроки, вести заметки и решать задачи в редакторе. войти
Уроки раздела
- Effect intro: Effect<A, E, R>, gen, runtime, первый probe
- Schema: parse-don't-validate, branded типы, transform
- Tagged-ошибки: actionable vs unactionable, catchTag, Cause, matchEffect
- Services и Layer: Context.Service, Layer, make, ManagedRuntime, MemoMap
- Resources: Scope, finalizers, acquireRelease, forkScoped, graceful shutdown
- Файберы и concurrency: fork, join, interrupt, Effect.all, Semaphore, race
- Координация: Deferred, Queue, PubSub, Semaphore, Latch
- Транзакции: TxRef, TxQueue, Effect.tx, ABA, тесты транзакций
- Stream и Channel: pull vs push, callback, buffer, grouped, merge, broadcast
- Batching: Request, RequestResolver, dataloader, кеш с TTL
- Runtime и Schedule: retry, repeat, timeout, race с fallback
- Production-обвязка: CLI, Terminal, HTTP-сервер
- Testing и code style: TestClock, dual, branded, Do, Data, финальный чек-лист
- DDD-типы: branded, opaque, smart constructors на Schema
- Decider pattern на Effect.gen: decide, evolve, initial
- Event Store на @effect/sql-pg: append-only, optimistic concurrency
- CQRS-проекции: Daemon, SubscriptionRef, live read-model
- Observability: логи, спаны, метрики, OpenTelemetry
- HttpClient и обвязка API: ретраи, схемы, middleware, CORS, OpenAPI
- Config, секреты и platform: FileSystem, Path, Command, KeyValueStore
- Schema, второй заход: рекурсия, все ошибки сразу, async-валидация, Arbitrary
- Schema как контракт для модели: JSON Schema, починка ответа, потоковый разбор
- Sink и продвинутый Stream: приёмники, пакеты, DLQ, состояние по ходу
- Стандартная библиотека Effect: равенство, коллекции, порядок, деньги, время
- Инструменты и отладка: LSP, диагностики в CI, layerinfo, Cause, файбер-дамп
- Архитектура бэкенда на Effect: слайсы, очередь на Postgres, outbox, воркер
- HKT и TypeLambda: один код над Effect, Stream и Result
Понятия курса
Понятия разобраны внутри уроков. Нажми на понятие, чтобы открыть урок, где оно объясняется.
Старт
- Effect.gen и pipe: композиция без вложенных flatMapДве формы записи: pipe(map, flatMap, tap) и Effect.gen с yield* как async/await. Когда выгоднее одна, когда другая.урок 1 · Effect intro: Effect<A, E, R>, gen, runtime, первый probeопираются: урок 2
- Effect<A, E, R>: три канала и runPromiseТип Effect как описание программы: канал успеха, канал ошибки, канал требований. Создание (succeed, fail, sync, promise) и запуск через Runtime.урок 1 · Effect intro: Effect<A, E, R>, gen, runtime, первый probeопираются: урок 2, урок 3, урок 4, урок 27
Данные и ошибки
- Branded-типы: Url, Interval, MonitorIdSchema.brand плюс filter превращают unknown в номинальный тип. Url не путается с произвольной строкой, MonitorId не сложить с другим id даже на уровне типа.урок 2 · Schema: parse-don't-validate, branded типы, transformопираются: урок 14
- catchTag, catchAll и матчинг по _tagEffect.catchTag('FetchError', handler), Effect.catchAll, Effect.match. Defects (исключения, не из канала) против expected errors.урок 3 · Tagged-ошибки: actionable vs unactionable, catchTag, Cause, matchEffect
- Schema, decodeUnknown, parse-don't-validateSchema как декларативный парсер: decodeUnknown превращает unknown в типизированное значение или ParseError. Trim, Number.from(String), Date.from(Number), составные структуры.урок 2 · Schema: parse-don't-validate, branded типы, transformопираются: урок 3, урок 14, урок 21, урок 24
- Tagged-ошибки: Data.TaggedError и канал Eclass FetchError extends Data.TaggedError('FetchError')<{ url: string }> {}. Тип ошибки виден в сигнатуре, дискриминируется по _tag, не теряется при композиции.урок 3 · Tagged-ошибки: actionable vs unactionable, catchTag, Cause, matchEffectопираются: урок 4, урок 11, урок 14
Сервисы и ресурсы
- Layer memoization, default services, Layer.freshLayer мемоизируется: один и тот же тег в графе строится один раз. Default services (Clock, Random, Console) уже есть в Runtime, через TestClock подменяем во всех тестах сразу.урок 4 · Services и Layer: Context.Service, Layer, make, ManagedRuntime, MemoMapопираются: урок 5
- Scope и acquireRelease: гарантированное освобождениеEffect.acquireRelease(open, close) гарантирует close при любом исходе: успех, ошибка, отмена. Scope собирает finalizers, Layer.scoped поднимает сервис с собственным временем жизни.урок 5 · Resources: Scope, finalizers, acquireRelease, forkScoped, graceful shutdownопираются: урок 6, урок 9, урок 16
- Services и Layer: dependency injection через канал RContext.Tag описывает интерфейс сервиса, Layer строит реализацию. Канал R больше не never, в него попадает каждый yield* Service. Layer.merge и Layer.provide собирают граф зависимостей.урок 4 · Services и Layer: Context.Service, Layer, make, ManagedRuntime, MemoMapопираются: урок 5, урок 6, урок 7, урок 10, урок 11, урок 12, урок 13, урок 15, урок 16, урок 18, урок 20, урок 25, урок 26
Параллелизм и координация
- Effect.all, Effect.race, концулентность с лимитомEffect.all с concurrency: 5 параллелит до пяти задач. Effect.race возвращает первого, остальные интерраптятся. forEach с concurrency: 'unbounded'.урок 6 · Файберы и concurrency: fork, join, interrupt, Effect.all, Semaphore, raceопираются: урок 7
- Файберы и structured concurrencyFiber это лёгкий поток выполнения над event loop. Effect.fork даёт фонового исполнителя, родительский scope владеет временем жизни детей. Никаких висячих promises.урок 6 · Файберы и concurrency: fork, join, interrupt, Effect.all, Semaphore, raceопираются: урок 7, урок 8, урок 11
- Deferred, Queue, PubSub, Semaphore, LatchПять примитивов для общения между файберами: одноразовый сигнал (Deferred), очередь (Queue), broadcast (PubSub), лимит конкуренции (Semaphore), стартовый барьер (Latch).урок 7 · Координация: Deferred, Queue, PubSub, Semaphore, Latchопираются: урок 8, урок 9, урок 10, урок 12, урок 17
- STM: атомарные транзакции над shared stateTRef и TQueue вместо Ref и Queue, когда несколько файберов меняют связанное состояние. Транзакция атомарна: либо вся применилась, либо повторилась.урок 8 · Транзакции: TxRef, TxQueue, Effect.tx, ABA, тесты транзакцийопираются: урок 9
Стримы и батчинг
- Effect.request и RequestResolver: батчинг и кешДешёвая абстракция dataloader: 100 параллельных Effect.request попадают в один батч-запрос, ResolverResolver сам решает как их объединить. Plus кеш с TTL.урок 10 · Batching: Request, RequestResolver, dataloader, кеш с TTL
- Sink и конвейеры: приёмники, пакеты по размеру и времени, DLQSink это вторая половина стрима: описание того, куда поток вливается. Готовые приёмники, свой через fold или forEach, несколько результатов за один проход через zip, пакетирование через aggregateWithin, недоставленное в отдельную очередь.урок 23 · Sink и продвинутый Stream: приёмники, пакеты, DLQ, состояние по ходу
- Stream, Channel и Sink: потоки и backpressureStream<A, E, R> это ленивый поток поверх Effect с pull-моделью и backpressure. Channel описывает вход, выход и итог завершения; собственное преобразование порций подключается через Stream.pipeThroughChannel. Sink собирает поток в значение.урок 9 · Stream и Channel: pull vs push, callback, buffer, grouped, merge, broadcastопираются: урок 10, урок 12, урок 13, урок 17, урок 23, урок 27
Production-обвязка
- Production-обвязка: @effect/cli, Terminal, HTTP-серверPulse получает обе поверхности сразу: CLI (@effect/cli, Args/Options/Subcommand через Schema, Prompt, Terminal с ANSI, SIGINT через Scope) и HTTP-сервер (@effect/platform-node/NodeHttpServer, HttpApi, эндпоинты /status, /api/monitors, SSE /events). Общий MainLive Layer, общий Runtime.урок 12 · Production-обвязка: CLI, Terminal, HTTP-серверопираются: урок 13, урок 19
- Runtime, Schedule, retry, repeat, timeoutSchedule это композируемое расписание: exponential backoff с jitter и cap, repeat каждые N секунд, retry на конкретный _tag. Runtime, кастомный исполнитель с предзагруженными layers.урок 11 · Runtime и Schedule: retry, repeat, timeout, race с fallbackопираются: урок 12, урок 13, урок 18, урок 26
Качество и тестирование
- Dual API, Data, Equal: идиомы и code styleDual API: Effect.map(eff, f) и eff.pipe(Effect.map(f)) это одна функция. Data.struct даёт structural equality. Когда брать Effect, когда хватит neverthrow.урок 13 · Testing и code style: TestClock, dual, branded, Do, Data, финальный чек-листопираются: урок 24
- TestClock, Layer.test, виртуальное времяTestClock.adjust('5 minutes') проматывает виртуальное время в тесте, Schedule и Effect.sleep подчиняются. Layer.test подменяет сервисы тестовыми реализациями.урок 13 · Testing и code style: TestClock, dual, branded, Do, Data, финальный чек-лист
DDD в Effect
- DDD-типы: branded, opaque, smart constructorsReservation-домен на Schema: ReservationId, GuestEmail, RoomNumber, DateRange с инвариантом checkIn<checkOut, Money с currency mismatch на типах. Парсим один раз на границе, дальше типы держат гарантии.урок 14 · DDD-типы: branded, opaque, smart constructors на Schemaопираются: урок 15
- CQRS-проекции: Daemon и SubscriptionRefРазделение write/read. Проекция как чистая (state, event) -> state, но материализованная как live view через SubscriptionRef. Daemon-файбер форкается под Scope, держит read-model в актуальном состоянии. UI подписывается через SubscriptionRef.changes, без polling.урок 17 · CQRS-проекции: Daemon, SubscriptionRef, live read-model
- Decider pattern на Effect.genКанон Жереми Шассена: decide + evolve + initial. Команды отвергаются, события навсегда. Reservation FSM (NotPlaced -> Reserved -> CheckedIn -> CheckedOut/Cancelled) как пять чистых функций. Verify(scenario) как стандартный тестовый паттерн.урок 15 · Decider pattern на Effect.gen: decide, evolve, initialопираются: урок 16
- Event Store на @effect/sql-pgAppend-only events в Postgres. Уникальность (stream_id, version) даёт optimistic concurrency. PgClient.layer плюс Schema-декод payload. Replay стрима через decider.evolve восстанавливает state без снимков.урок 16 · Event Store на @effect/sql-pg: append-only, optimistic concurrencyопираются: урок 17, урок 26
Observability и обвязка
- Config и Redacted: конфигурация как декларация, секреты без утечекConfig описывает, что прочитать, ConfigProvider откуда. Примитивы сразу дают готовые типы (URL, Duration, port), недостающие ключи приходят все сразу одним сообщением, секреты живут в Redacted и печатаются как redacted.урок 20 · Config, секреты и platform: FileSystem, Path, Command, KeyValueStoreопираются: урок 21
- Platform: FileSystem, Path, Command, KeyValueStoreФайлы, пути, внешние процессы и key-value хранилище как сервисы, а не как импорты из node. Тот же код работает в Node, Bun и браузере, подменяется в тестах и получает отмену с очисткой ресурсов бесплатно.урок 20 · Config, секреты и platform: FileSystem, Path, Command, KeyValueStoreопираются: урок 23
- Логи и спаны: annotateLogs, Logger, withSpan, Effect.fnЛог как эффект: уровни, Cause вместо строки, аннотации на кусок программы вместо протаскивания requestId. Logger как сервис: json, pretty, свой через Logger.make, батчинг. Спаны через withSpan и Effect.fn, дерево вызовов с одним traceId.урок 18 · Observability: логи, спаны, метрики, OpenTelemetryопираются: урок 19
- Метрики: counter, gauge, histogram, frequency, экспортШесть типов метрик и вопрос, на который отвечает каждый. Комбинаторы поверх эффекта (trackDuration, trackErrorWith) вместо ручных update. Теги через Effect.tagMetrics. Экспорт одним NodeSdk.layer в OTLP и Prometheus, либо свой /metrics через Metric.snapshot.урок 18 · Observability: логи, спаны, метрики, OpenTelemetryопираются: урок 25
- Обвязка API: middleware, авторизация, CORS, OpenAPI, производный клиентHttpApiMiddleware.Tag описывает middleware контрактом из трёх полей: чем падает, какой сервис кладёт в контекст, откуда берёт данные безопасности. CORS и страница документации подключаются слоем. Клиент к своему API выводится из его описания и отдаёт типизированные ошибки.урок 19 · HttpClient и обвязка API: ретраи, схемы, middleware, CORS, OpenAPIопираются: урок 20
- HttpClient: схемы ответов, retryTransient, подмена в тестахИсходящий HTTP как сервис: базовый URL и заголовки настраиваются один раз через mapRequest, статусы фильтруются filterStatusOk, временные сбои повторяются retryTransient. Ответ декодируется схемой, поэтому в канале ошибок отдельно живут сеть, статус и нарушенный контракт.урок 19 · HttpClient и обвязка API: ретраи, схемы, middleware, CORS, OpenAPIопираются: урок 22
Schema вглубь
- Рекурсия, накопление ошибок и асинхронная валидация в SchemaSchema.suspend для рекурсивных структур, errors: all для форм, TreeFormatter и ArrayFormatter для двух разных читателей, filterEffect для проверок, которым нужен сервис.урок 21 · Schema, второй заход: рекурсия, все ошибки сразу, async-валидация, Arbitraryопираются: урок 22
- Эволюция схем, jsonb и property-тесты из схемыСмена формата данных описывается объединением версий плюс transform, поэтому остальной код знает только последнюю версию. parseJson закрывает jsonb-колонку в обе стороны. Arbitrary.make превращает схему в генератор для round trip тестов.урок 21 · Schema, второй заход: рекурсия, все ошибки сразу, async-валидация, Arbitrary
- Schema как контракт для модели: JSON Schema, починка ответа, потоковый разборJSONSchema.make превращает схему в спецификацию для языковой модели, а аннотации переносят документацию полей из промпта в саму схему. Невалидный ответ чинится обратной связью: замечания из ArrayFormatter уходят модели дословно.урок 22 · Schema как контракт для модели: JSON Schema, починка ответа, потоковый разбор
Инструментарий
- Инструменты: LSP-диагностики, layerinfo, Cause на отладке, снимок файберовДлинная ошибка типов разбирается по алгоритму: канал, фраза про never, снизу вверх. LSP-плагин ловит floatingEffect и missingEffectContext за миллисекунды, CLI выносит те же диагностики в CI, layerinfo показывает граф слоёв.урок 25 · Инструменты и отладка: LSP, диагностики в CI, layerinfo, Cause, файбер-дамп
- Стандартная библиотека: равенство, HashMap, Order, BigDecimal, DateTimeТипы, которые чинят три врождённые беды JavaScript: ссылочное равенство, двоичные дроби и дату без пояса. Data.Class и HashMap, Order и Equivalence как значения, BigDecimal для денег, DateTime поверх Clock.урок 24 · Стандартная библиотека Effect: равенство, коллекции, порядок, деньги, время
Архитектура
- Архитектура бэкенда: слайсы, очередь на Postgres, outbox, воркерРазбор боевого платёжного шлюза на Effect: вертикальные слайсы внутри горизонтальных границ, kysely как билдер поверх SqlClient, ManagedRuntime как мост в чужой фреймворк, тонкие bin-точки входа с REPL, очередь на Postgres с тремя слоями идемпотентности, transactional outbox, шедулер с лестницей ретраев и якорем даты.урок 26 · Архитектура бэкенда на Effect: слайсы, очередь на Postgres, outbox, воркер
Типы и полиморфизм
- HKT и TypeLambda: один код над Effect, Stream и ResultЭмуляция higher-kinded types в Effect: TypeLambda из четырёх слотов, Kind как применение, свои type классы (Monad, UnifiedTypeClass) и одна полиморфная функция над Effect и Stream.урок 27 · HKT и TypeLambda: один код над Effect, Stream и Result