раздел 25
Effect-TS
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: pull vs push, asyncPush, 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, воркер
Темы и кластеры
Старт
- Effect.gen и pipe: композиция без вложенных flatMapДве формы записи: pipe(map, flatMap, tap) и Effect.gen с yield* как async/await. Когда выгоднее одна, когда другая.есть урок
- Effect<A, E, R>: три канала и runPromiseТип Effect как описание программы: канал успеха, канал ошибки, канал требований. Создание (succeed, fail, sync, promise) и запуск через Runtime.есть урок
Данные и ошибки
- Branded-типы: Url, Interval, MonitorIdSchema.brand плюс filter превращают unknown в номинальный тип. Url не путается с произвольной строкой, MonitorId не сложить с другим id даже на уровне типа.есть урок
- catchTag, catchAll и матчинг по _tagEffect.catchTag('FetchError', handler), Effect.catchAll, Effect.match. Defects (исключения, не из канала) против expected errors.есть урок
- Schema, decodeUnknown, parse-don't-validateSchema как декларативный парсер: decodeUnknown превращает unknown в типизированное значение или ParseError. Trim, Number.from(String), Date.from(Number), составные структуры.есть урок
- Tagged-ошибки: Data.TaggedError и канал Eclass FetchError extends Data.TaggedError('FetchError')<{ url: string }> {}. Тип ошибки виден в сигнатуре, дискриминируется по _tag, не теряется при композиции.есть урок
Сервисы и ресурсы
- Layer memoization, default services, Layer.freshLayer мемоизируется: один и тот же тег в графе строится один раз. Default services (Clock, Random, Console) уже есть в Runtime, через TestClock подменяем во всех тестах сразу.есть урок
- Scope и acquireRelease: гарантированное освобождениеEffect.acquireRelease(open, close) гарантирует close при любом исходе: успех, ошибка, отмена. Scope собирает finalizers, Layer.scoped поднимает сервис с собственным временем жизни.есть урок
- Services и Layer: dependency injection через канал RContext.Tag описывает интерфейс сервиса, Layer строит реализацию. Канал R больше не never, в него попадает каждый yield* Service. Layer.merge и Layer.provide собирают граф зависимостей.есть урок
Параллелизм и координация
- Effect.all, Effect.race, концулентность с лимитомEffect.all с concurrency: 5 параллелит до пяти задач. Effect.race возвращает первого, остальные интерраптятся. forEach с concurrency: 'unbounded'.есть урок
- Файберы и structured concurrencyFiber это лёгкий поток выполнения над event loop. Effect.fork даёт фонового исполнителя, родительский scope владеет временем жизни детей. Никаких висячих promises.есть урок
- Deferred, Queue, PubSub, Semaphore, LatchПять примитивов для общения между файберами: одноразовый сигнал (Deferred), очередь (Queue), broadcast (PubSub), лимит конкуренции (Semaphore), стартовый барьер (Latch).есть урок
- STM: атомарные транзакции над shared stateTRef и TQueue вместо Ref и Queue, когда несколько файберов меняют связанное состояние. Транзакция атомарна: либо вся применилась, либо повторилась.есть урок
Стримы и батчинг
- Effect.request и RequestResolver: батчинг и кешДешёвая абстракция dataloader: 100 параллельных Effect.request попадают в один батч-запрос, ResolverResolver сам решает как их объединить. Plus кеш с TTL.есть урок
- Sink и конвейеры: приёмники, пакеты по размеру и времени, DLQSink это вторая половина стрима: описание того, куда поток вливается. Готовые приёмники, свой через fold или forEach, несколько результатов за один проход через zip, пакетирование через aggregateWithin, недоставленное в отдельную очередь.есть урок
- Stream и Sink: backpressure из коробкиStream<A, E, R> это асинхронный итератор поверх Effect: pull-based, с backpressure. Sink собирает поток в значение. Окна, групповые агрегации, fromAsyncIterable.есть урок
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.есть урок
- Runtime, Schedule, retry, repeat, timeoutSchedule это композируемое расписание: exponential backoff с jitter и cap, repeat каждые N секунд, retry на конкретный _tag. Runtime, кастомный исполнитель с предзагруженными layers.есть урок
Качество и тестирование
- Dual API, Data, Equal: идиомы и code styleDual API: Effect.map(eff, f) и eff.pipe(Effect.map(f)) это одна функция. Data.struct даёт structural equality. Когда брать Effect, когда хватит neverthrow.есть урок
- TestClock, Layer.test, виртуальное времяTestClock.adjust('5 minutes') проматывает виртуальное время в тесте, Schedule и Effect.sleep подчиняются. Layer.test подменяет сервисы тестовыми реализациями.есть урок
DDD в Effect
- DDD-типы: branded, opaque, smart constructorsReservation-домен на Schema: ReservationId, GuestEmail, RoomNumber, DateRange с инвариантом checkIn<checkOut, Money с currency mismatch на типах. Парсим один раз на границе, дальше типы держат гарантии.есть урок
- CQRS-проекции: Daemon и SubscriptionRefРазделение write/read. Проекция как чистая (state, event) -> state, но материализованная как live view через SubscriptionRef. Daemon-файбер форкается под Scope, держит read-model в актуальном состоянии. UI подписывается через SubscriptionRef.changes, без polling.есть урок
- Decider pattern на Effect.genКанон Жереми Шассена: decide + evolve + initial. Команды отвергаются, события навсегда. Reservation FSM (NotPlaced -> Reserved -> CheckedIn -> CheckedOut/Cancelled) как пять чистых функций. Verify(scenario) как стандартный тестовый паттерн.есть урок
- Event Store на @effect/sql-pgAppend-only events в Postgres. Уникальность (stream_id, version) даёт optimistic concurrency. PgClient.layer плюс Schema-декод payload. Replay стрима через decider.evolve восстанавливает state без снимков.есть урок
Observability и обвязка
- Config и Redacted: конфигурация как декларация, секреты без утечекConfig описывает, что прочитать, ConfigProvider откуда. Примитивы сразу дают готовые типы (URL, Duration, port), недостающие ключи приходят все сразу одним сообщением, секреты живут в Redacted и печатаются как redacted.есть урок
- Platform: FileSystem, Path, Command, KeyValueStoreФайлы, пути, внешние процессы и key-value хранилище как сервисы, а не как импорты из node. Тот же код работает в Node, Bun и браузере, подменяется в тестах и получает отмену с очисткой ресурсов бесплатно.есть урок
- Логи и спаны: annotateLogs, Logger, withSpan, Effect.fnЛог как эффект: уровни, Cause вместо строки, аннотации на кусок программы вместо протаскивания requestId. Logger как сервис: json, pretty, свой через Logger.make, батчинг. Спаны через withSpan и Effect.fn, дерево вызовов с одним traceId.есть урок
- Метрики: counter, gauge, histogram, frequency, экспортШесть типов метрик и вопрос, на который отвечает каждый. Комбинаторы поверх эффекта (trackDuration, trackErrorWith) вместо ручных update. Теги через Effect.tagMetrics. Экспорт одним NodeSdk.layer в OTLP и Prometheus, либо свой /metrics через Metric.snapshot.есть урок
- Обвязка API: middleware, авторизация, CORS, OpenAPI, производный клиентHttpApiMiddleware.Tag описывает middleware контрактом из трёх полей: чем падает, какой сервис кладёт в контекст, откуда берёт данные безопасности. CORS и страница документации подключаются слоем. Клиент к своему API выводится из его описания и отдаёт типизированные ошибки.есть урок
- HttpClient: схемы ответов, retryTransient, подмена в тестахИсходящий HTTP как сервис: базовый URL и заголовки настраиваются один раз через mapRequest, статусы фильтруются filterStatusOk, временные сбои повторяются retryTransient. Ответ декодируется схемой, поэтому в канале ошибок отдельно живут сеть, статус и нарушенный контракт.есть урок
Schema вглубь
- Рекурсия, накопление ошибок и асинхронная валидация в SchemaSchema.suspend для рекурсивных структур, errors: all для форм, TreeFormatter и ArrayFormatter для двух разных читателей, filterEffect для проверок, которым нужен сервис.есть урок
- Эволюция схем, jsonb и property-тесты из схемыСмена формата данных описывается объединением версий плюс transform, поэтому остальной код знает только последнюю версию. parseJson закрывает jsonb-колонку в обе стороны. Arbitrary.make превращает схему в генератор для round trip тестов.есть урок
- Schema как контракт для модели: JSON Schema, починка ответа, потоковый разборJSONSchema.make превращает схему в спецификацию для языковой модели, а аннотации переносят документацию полей из промпта в саму схему. Невалидный ответ чинится обратной связью: замечания из ArrayFormatter уходят модели дословно.есть урок
Инструментарий
- Инструменты: LSP-диагностики, layerinfo, Cause на отладке, снимок файберовДлинная ошибка типов разбирается по алгоритму: канал, фраза про never, снизу вверх. LSP-плагин ловит floatingEffect и missingEffectContext за миллисекунды, CLI выносит те же диагностики в CI, layerinfo показывает граф слоёв.есть урок
- Стандартная библиотека: равенство, HashMap, Order, BigDecimal, DateTimeТипы, которые чинят три врождённые беды JavaScript: ссылочное равенство, двоичные дроби и дату без пояса. Data.Class и HashMap, Order и Equivalence как значения, BigDecimal для денег, DateTime поверх Clock.есть урок