Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 19 из 25 ~22 мин +50 XP

Privacy-политики

Интерсептор восемнадцатого урока прячет строки из выборок, но не отвечает на главный вопрос доступа: имеет ли этот контекст право вообще — читать, создавать, удалять. Сегодня — ent privacy: слой политик, который вы пишете один раз в схеме и который исполняется всегда, где бы в кодовой базе ни вызывался Query или мутация. Разберём интерфейс ent.Policy с EvalQuery и EvalMutation, три решения — privacy.Allow, privacy.Deny и privacy.Skip, — соберём deny по умолчанию хвостовым AlwaysDenyRule, напишем правила viewer-контекста: DenyIfNoViewer и AllowIfAdmin, запретим жёсткое удаление через DenyMutationOperationRule — зеркальный аналог hook.Reject — и научимся тестировать политику через privacy.DecisionContext и errors.Is. ent privacy — это ent безопасность на уровне клиента: от забытых Where, от новых хендлеров, обошедших старые проверки, от операций без пользователя в контексте. По канону курса: анна, борис, вера, группа go-ninja и счётчик posts_count.

Содержание урока · 12 разделов

Маршрут урока — от «зачем» до тестов. Сначала — мотив: главный преимущество privacy layer в том, что политика объявлена один раз и исполняется всегда; ни один новый хендлер не обойдёт её, потому что обойти некуда — путь лежит через клиент, а клиент обязан спросить политику. Затем — механика включения: фича-флаг privacy и сгенерированный пакет ent/privacy, вместе с живой ошибкой забытого пустого импорта runtime. Третья часть — термины: Policy, правила, решения Allow/Deny/Skip и порядок их оценки; здесь же честный разбор «deny по умолчанию» — что ent делает сам, а что вы обязаны достроить правилом. Четвёртая — два рабочих правила: DenyIfNoViewer и AllowIfAdmin на privacy.ContextQueryMutationRule, и почему DenyIfNoViewer обязан стоять первым. Пятая — где политика сидит в конвейере: EvalQuery до интерсепторов, EvalMutation в слоте hooks[0] до всех хуков из семнадцатого урока. Шестая — сужение правил по операциям: OnMutationOperation и DenyMutationOperationRule. Финал — тесты политики: DecisionContext, errors.Is и матрица кейсов. Если вы искали «ent privacy» или «ent безопасность» — это тот урок.

Зачем privacy: политика один раз и всегда#

До этого урока доступ к данным держался на дисциплине: хендлеры сами решали, что кому показывать, фильтры сами не забывали Where, админские операции сами проверяли роль. Каждый новый эндпоинт — новая возможность забыть проверку, а рукописный обход ent-клиента — уже не защита вовсе, как мы честно считали ещё с валидаторов третьего урока. ent privacy переворачивает ответственность: политика объявляется в схеме — там же, где поля, рёбра, хуки и интерсепторы, — и исполняется на каждой операции чтения и записи этого типа, из любого места кодовой базы, у любого собранного клиента. Это ent безопасность уровня графа: нельзя создать пользователя без viewer-а нельзя, даже если завтра коллега напишет новый сервис и забудет про роли — операция упрётся в слой, который жить в схеме, а не в хендлерах. Граница ответственности с интерсептором из восемнадцатого урока строгая: интерсептор фильтрует — прячет строки из выборок; политика решает — допускает или отменяет операцию целиком. Оба инструмента исполняются до SQL, но отвечают на разные вопросы: «что вы видите» и «можно ли вам это».

Фича-флаг и пакет ent/privacy#

Privacy — фича-флаг генератора, как intercept из восемнадцатого урока. В ent/generate.go добавьте --feature privacy: go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature privacy ./schema — и запустите go generate из седьмого урока. Генератор приносит пакет ent/privacy с готовым словарём: решения privacy.Allow, privacy.Deny и privacy.Skip — это sentinel-ошибки, то есть обычные error-значения, которые правила возвращают; хелперы Allowf, Denyf и Skipf — те же решения с форматированной причиной в тексте; AlwaysAllowRule и AlwaysDenyRule — правила-заглушки; ContextQueryMutationRule — фабрика правила из функции контекста; OnMutationOperation и DenyMutationOperationRule — сужение и запрет операций; DecisionContext — решение, вшитое в контекст. Второй обязательный шаг, за которым следит документация: если в схеме объявлен Policy(), в main нужен пустой импорт import _ «ваш-модуль/ent/runtime» — из-за возможного цикла импортов между схемой и пакетом ent рантайм обязан инициализироваться явно. Забудете — ent не уронит компиляцию, но первый же запрос честно откажет: user.Policy == nil, текст ошибки называет причину дословно — forgotten import ent/runtime.

Проверка: забытый пустой импорт

Policy() объявлен в схеме, но import _ ent/runtime в main вы забыли. Что произойдёт?

Термины: Policy, правила, решения#

ent.Policy — интерфейс из двух методов: EvalQuery(ctx, ent.Query) error — read-policy, EvalMutation(ctx, ent.Mutation) error — write-policy. В схеме вы не реализуете его руками: собираете из правил через privacy.Policy{Query: ..., Mutation: ...}, где QueryPolicy и MutationPolicy — списки правил. Правило — то же правило из мира privacy: объект с EvalQuery и/или EvalMutation, возвращающий одно из трёх решений. Решения — обычные ошибки, и семантика у них точная. privacy.Allow — «разрешить»: оценка останавливается, последующие правила не вызываются, операция допущена. privacy.Deny — «запретить»: оценка останавливается, операция отменена; любое другое возвращённое правило правило-ошибку работает как Deny. privacy.Skip — «нет мнения»: переход к следующему правилу; эквивалент возврата nil. Правила оцениваются строго в порядке объявления. И теперь честная деталь, которую надо знать про ent: если ВСЕ правила Skip — оценка завершается без ошибки, то есть операция разрешена. ent privacy не встраивает «запрещено, если не разрешено» — вы строите её сами, хвостовым правилом privacy.AlwaysDenyRule(): всё, что не пропустили предыдущие правила, отвергается. Это и есть deny по умолчанию как рабочий паттерн — и дальше в уроке он стоит в каждой политике.

Политика в схеме: deny по умолчанию

GO
// в схеме User (ent/schema/user.go):
func (User) Policy() ent.Policy {
        return privacy.Policy{
                Query: privacy.QueryPolicy{
                        rule.DenyIfNoViewer(), // первым: безопасность доступа
                        rule.AllowIfAdmin(),
                },
                Mutation: privacy.MutationPolicy{
                        rule.DenyIfNoViewer(),
                        rule.AllowIfAdmin(),
                        privacy.AlwaysDenyRule(), // хвост deny-по-умолчанию
                },
        }
}

privacy.Policy собирает списки правил: QueryPolicy для чтения, MutationPolicy для записи. Чтение для всех, у кого есть viewer; мутации — только админам; всё остальное — AlwaysDenyRule отвергает. Уберите хвост — и мутация от не-админа пройдёт: правила Skip-нули до конца списка, а пустая оценка = разрешено. Хвостовой AlwaysDenyRule — контракт deny по умолчанию, а не украшение.

viewer-контекст и два правила

GO
// viewer/viewer.go: пользователь операции в контексте.
type UserViewer struct{ Role string }

func NewContext(parent context.Context, v *UserViewer) context.Context {
        return context.WithValue(parent, viewerKey{}, v)
}
func FromContext(ctx context.Context) *UserViewer {
        v, _ := ctx.Value(viewerKey{}).(*UserViewer)
        return v
}

// rule/rule.go: два правила политики.
func DenyIfNoViewer() privacy.QueryMutationRule {
        return privacy.ContextQueryMutationRule(func(ctx context.Context) error {
                if viewer.FromContext(ctx) == nil {
                        return privacy.Denyf("ent/privacy: нет viewer в контексте")
                }
                return privacy.Skip
        })
}

func AllowIfAdmin() privacy.QueryMutationRule {
        return privacy.ContextQueryMutationRule(func(ctx context.Context) error {
                if v := viewer.FromContext(ctx); v != nil && v.Role == "admin" {
                        return privacy.Allow
                }
                return privacy.Skip
        })
}

viewer — ваш пакет: ent не знает, кто такой «пользователь», он умеет читать context.Context. NewContext кладёт viewer, FromContext достаёт. ContextQueryMutationRule оборачивает функцию контекста в правило для чтения и записи сразу. DenyIfNoViewer возвращает Denyf — отказ с причиной; AllowIfAdmin — Allow или Skip. Двойная проверка v != nil в AllowIfAdmin — страховка на случай, когда правило стоит раньше DenyIfNoViewer.

Проверка: порядок правил

Почему DenyIfNoViewer обязан стоять в списке раньше AllowIfAdmin?

Мутация от не-админа

Прочитайте код и выберите, что он выведет:

// политика User: Mutation = [DenyIfNoViewer, AllowIfAdmin, AlwaysDenyRule]
ctx := context.Background() // viewer-а нет
ctxView := viewer.NewContext(ctx, viewer.UserViewer{Role: "view"})

err := client.User.Create().
        SetName("борис").
        SetEmail("boris@entgo.io").
        Exec(ctxView)

Где политика сидит в конвейере#

Две точки исполнения, обе до всего остального. Чтение: перед исполнением выборки ent вызывает Policy.EvalQuery — раньше интерсепторов из восемнадцатого урока и раньше SQL; Count, Exist, Only и обходы рёбер проходят через ту же проверку, потому что это всё — Query-пути. Отказ — операция не исполняется вовсе: ни SELECT, ни частичных данных. Запись: политика вшивается в слот hooks[0] — самый внешний слой конвейера мутаций — и EvalMutation исполняется до всех схемных и Use-хуков из семнадцатого урока; ни один ваш хук не увидит мутацию, которую политика отвергла. Отсюда два практических следствия. Первое: privacy.Deny не обрабатывается как ValidationError или ConstraintError — это собственный sentinel, ловите errors.Is(err, privacy.Deny), и он не подменяется MaskNotFound-ом. Второе: политика и транзакции из шестнадцатого урока живут дружно — проверка исполняется на границе операции, до WithTx, и отказ не оставляет после себя открытой транзакции; внутри tx-клиента политики сработают снова, потому что tx.Client() — тот же граф, те же правила.

Решения на операции: OnMutationOperation#

Privacy-список из «глобальных» правил — не единственная форма; правила можно сужать до конкретных операций мутации — знакомый приём по hook.On из семнадцатого урока. privacy.OnMutationOperation(rule, op) исполняет rule, только если m.Op().Is(op) совпал, иначе возвращает Skip — правило молчит. На нём построен готовый helper privacy.DenyMutationOperationRule(op): запрет конкретной операции с ошибкой Denyf. Вместе они закрывают классический сценарий — политику жёсткого удаления, которую четырнадцатый урок собирал из hook.Reject: там запрет жил в коде приложения и его надо было не забыть зарегистрировать, здесь — в схеме, и он работает у всех клиентов графа без исключений. Симметрия с семнадцатым уроком дословная: hook.Reject и DenyMutationOperationRule решают одну задачу на двух уровнях — рантайм и схема; если запрет обязан быть законом графа — ему место в политике. Отдельный приём для сложных правил — privacy.FilterFunc: правило получает generic-фильтр запроса и вшивает в него предикат вроде «только свой тенант», превращая доступ в ограничение выборки; это мост к мультиарендности двадцать второго урока, здесь — только знать в лицо.

Запрет жёсткого удаления политикой

GO
// Group: жёсткое удаление закрыто на уровне схемы.
func (Group) Policy() ent.Policy {
        return privacy.Policy{
                Mutation: privacy.MutationPolicy{
                        privacy.DenyMutationOperationRule(
                                ent.OpDelete | ent.OpDeleteOne,
                        ),
                },
        }
}

// попытка из любого места кодовой базы:
err := client.Group.DeleteOneID(goNinjaID).Exec(ctx)
// errors.Is(err, privacy.Deny) == true, DELETE не исполнен

DenyMutationOperationRule(ent.OpDelete | ent.OpDeleteOne) — зеркало hook.Reject(ent.OpDelete, ent.OpDeleteOne) из семнадцатого урока, но в схеме: идёт в сгенерированный код и не зависит от регистраций в main. «Удаление» группы go-ninja теперь возможно только мягким путём — SetDeletedAt через Update из тринадцатого урока — а жёсткий путь возвращает Deny до всякого SQL.

Проверка: Reject против Deny

Чем privacy.DenyMutationOperationRule отличается от hook.Reject?

Каталог: API privacy#

ИнструментЧто даётНюанс
privacy.Allowрешение «разрешить»останавливает оценку, операция допущена
privacy.Denyрешение «запретить»sentinel-ошибка; ловится errors.Is
privacy.Skipрешение «нет мнения»переход к следующему правилу; равен nil
Allowf/Denyf/Skipfрешения с причиной в текстелоги читаются без трассировки
AlwaysAllowRule()правило «всегда да»открытые на чтение сущности
AlwaysDenyRule()правило «всегда нет»хвост deny-по-умолчанию: обязателен
ContextQueryMutationRuleправило из func(ctx) errorviewer-проверки без дублей на Query/Mutation
QueryRule / MutationRuleинтерфейсы правил чтения и записиQueryPolicy/MutationPolicy — их списки
ent.PolicyEvalQuery + EvalMutationобъявляется в схеме методом Policy()
OnMutationOperationисполнение правила только на операциианалог hook.On из семнадцатого урока
DenyMutationOperationRuleзапрет конкретной операциианалог hook.Reject на уровне схемы
DecisionContextрешение, вшитое в контекстобход политики в тестах и внутренних запросах

Тесты политики#

Политика, как и вся защита, требует доказательств, и ent даёт для них два инструмента. Первый — privacy.DecisionContext(ctx, privacy.Allow): вшивает решение прямо в контекст, и оценка правил не запускается вовсе — операция исполняется так, будто первое правило вернуло Allow. Это способ протестировать саму логику приложения отдельно от политик: в тестах и внутренних служебных запросах — как в примере документации, где правило тенанта делает служебную выборку через DecisionContext, чтобы не зациклиться на собственной политике. Второй — errors.Is(err, privacy.Deny): отказ политики — это sentinel-ошибка, и тест обязан проверять именно её, а не текст. Рабочая матрица кейсов для политики User: контекст без viewer — Deny на мутации; viewer с ролью view — Deny на мутации, чтение разрешено; admin — всё разрешено; DecisionContext — всё разрешено без правил. Тестовый клиент — enttest.Open с SQLite, механику которого подробно разберёт двадцать третий урок (здесь упоминаем текстом): каждый кейс — три строки. И правило гигиены: политика тестируется на каждом типе схемы отдельно — наследования политик нет, mixin-политики из документации вшиваются в каждую схему как отдельные правила, и deny-хвост одной схемы не спасает соседнюю.

Тест политики: матрица кейсов

GO
func TestUserPolicy(t *testing.T) {
        client := enttest.Open(t, dialect.SQLite,
                "file:ent?mode=memory&_fk=1")
        defer client.Close()

        // 1. без viewer мутация запрещена:
        err := client.User.Create().SetName("x").Exec(ctx)
        require.True(t, errors.Is(err, privacy.Deny))

        // 2. view-роль читает, но не пишет:
        view := viewer.NewContext(ctx, viewer.UserViewer{Role: "view"})
        require.NotZero(t, client.User.Query().CountX(view))
        err = client.User.Create().SetName("x").Exec(view)
        require.True(t, errors.Is(err, privacy.Deny))

        // 3. админ пишет:
        admin := viewer.NewContext(ctx, viewer.UserViewer{Role: "admin"})
        require.NoError(t, client.User.Create().
                SetName("анна").SetEmail("anna@entgo.io").Exec(admin))

        // 4. DecisionContext обходит политику целиком:
        free := privacy.DecisionContext(ctx, privacy.Allow)
        require.NoError(t, client.User.Create().SetName("y").Exec(free))
}

Четыре кейса — четыре строки: Deny без viewer, Deny на записи при view, Allow админу, обход через DecisionContext. errors.Is — обязательная форма проверки: privacy.Deny — значение-ошибка, и ==-сравнение с обёрткой от Denyf не сработает. enttest.Open поднимает SQLite в памяти — подробный разбор тестовой инфраструктуры ent оставлен двадцать третьему уроку.

Чеклист privacy#

  • Политика — в схеме, один раз: func (T) Policy() ent.Policy; она исполняется на каждой операции типа у любого клиента — в этом весь смысл privacy layer.
  • Deny по умолчанию достраивается руками: хвостовым privacy.AlwaysDenyRule(); все Skip подряд = операция разрешена — пустой список политики ничего не запрещает.
  • DenyIfNoViewer — первым правилом: AllowIfAdmin обязан встретить viewer-а гарантированно, а не «если повезёт с порядком».
  • Ловите отказы через errors.Is(err, privacy.Deny): это sentinel-ошибка, а не строка и не ValidationError.
  • DenyMutationOperationRule(ent.OpDelete | ent.OpDeleteOne) — схемная версия hook.Reject: жёсткое удаление из четырнадцатого урока закрывается политикой.
  • import _ ent/runtime в main — обязательная строка при Policy() в схеме; ошибка «forgotten import ent/runtime?» всплывёт на первом запросе, а не на компиляции.
  • DecisionContext(ctx, privacy.Allow) — для тестов и служебных запросов; в продакшене контекст с вшитым Allow — дыра размером с приложение.
  • Тестируйте каждый тип отдельно: наследования политик нет, mixin-правила дублируются в каждую схему, deny-хвост одного типа не защищает соседний.

Соберите правило AllowIfAdmin

Впишите интерфейс правила, фабрику из контекста и решение для админа.

func AllowIfAdmin() privacy. { return privacy.(func(ctx context.Context) error { if v := viewer.FromContext(ctx); v != nil && v.Role == "admin" { return privacy. } return privacy.Skip }) }

Жизнь мутации под политикой

Соберите путь операции от контекста до отказа.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Что дальше#

Вы умеете строить ent безопасность на уровне графа: privacy-фича-флаг и пакет ent/privacy, ent.Policy с EvalQuery и EvalMutation, три решения Allow/Deny/Skip и порядок их оценки, deny по умолчанию как сознательный паттерн с AlwaysDenyRule, viewer-контекст с DenyIfNoViewer и AllowIfAdmin, сужение правил OnMutationOperation и запрет операций DenyMutationOperationRule, тесты через DecisionContext и errors.Is. Политика отвечает «можно ли», но не «быстро ли»: как только доступ открыт, выборка исполняется со скоростью своих индексов — и WHERE от частичного фильтра из восемнадцатого урока, и уникальность email, и сортировка ленты упрутся в полнотабличный скан, если индексов нет. Дальше — индексы и аннотации двадцатого урока: index.Fields и Edges, составные уникальные, StorageKey, entsql-аннотации Desc, IndexType и IndexWhere — и обещанный четырнадцатым уроком частичный индекс на живых строках. Домашнее задание ниже заставит политику отказать в правильном месте и докажет это тестом. Полный план из 25 уроков — на странице курса, хуки и hook.Reject — в семнадцатом уроке, мягкое удаление — в четырнадцатом, валидаторы и граница с базой — в третьем, транзакции — в шестнадцатом.

Карточки: privacy

Термин

ent.Policy и его два метода

нажмите, чтобы перевернуть
Определение

Интерфейс EvalQuery(ctx, ent.Query) error и EvalMutation(ctx, ent.Mutation) error. В схеме собирается privacy.Policy{Query: QueryPolicy{...}, Mutation: MutationPolicy{...}} и объявляется методом func (T) Policy() ent.Policy. Оценка — на каждой операции типа, у любого клиента: политика один раз и всегда.

1 / 6

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • ent privacy — слой политик в схеме: политика объявляется один раз и исполняется на каждой операции чтения и записи типа, у любого клиента; обойти её из кодовой базы нельзя — путь лежит через клиент.
  • Включение: --feature privacy в generate (фича-флаг, как intercept); генератор приносит пакет ent/privacy с Allow/Deny/Skip, Allowf/Denyf/Skipf, AlwaysAllowRule/AlwaysDenyRule, ContextQueryMutationRule, OnMutationOperation, DenyMutationOperationRule, DecisionContext.
  • ent.Policy — EvalQuery (read-policy) и EvalMutation (write-policy); правила оцениваются в порядке объявления: Allow или Deny останавливают оценку, Skip (равен nil) — переход к следующему.
  • Deny по умолчанию — паттерн, а не свойство: все Skip = операция разрешена; хвостовое AlwaysDenyRule достраивает «всё, что не разрешено — запрещено»; пустой список ничего не запрещает.
  • DenyIfNoViewer — первым правилом: гарантирует AllowIfAdmin-у наличие viewer-а; viewer — ваш пакет поверх context.Context (NewContext/FromContext).
  • Исполнение: EvalQuery — до интерсепторов и SQL (включая Count/Exist); EvalMutation — в слоте hooks[0] до всех хуков из семнадцатого урока; отказ — до всякого SQL.
  • OnMutationOperation сужает правило до операции, DenyMutationOperationRule(ent.OpDelete|ent.OpDeleteOne) — схемный аналог hook.Reject: жёсткое удаление закрыто у всех клиентов.
  • Тесты: errors.Is(err, privacy.Deny) для отказов; privacy.DecisionContext(ctx, privacy.Allow) для обхода политики в тестах и служебных запросах; матрица кейсов — без viewer, view, admin, decision.

Домашнее задание

Работайте на базе из восьмого урока — анна, борис, вера, их посты и группа go-ninja. Первое: включите --feature privacy, запустите go generate и найдите в ent пакет privacy: запишите, какие решения и хелперы он объявляет. Второе: соберите пакеты viewer (UserViewer{Role}, NewContext, FromContext) и rule (DenyIfNoViewer, AllowIfAdmin) из урока, объявите политику User с хвостовым AlwaysDenyRule на мутациях и прогоните матрицу из четырёх кейсов — без viewer, view, admin, DecisionContext; каждый кейс оформите тестом с errors.Is(err, privacy.Deny) или require.NoError. Третье: снимите AlwaysDenyRule и повторите кейс view-записи: мутация пройдёт; верните хвост и запишите в коммент, почему deny по умолчанию — это контракт, который вы достраиваете, а не настройка ent. Четвёртое: объявите на Group политику с DenyMutationOperationRule(ent.OpDelete|ent.OpDeleteOne) и попробуйте DeleteOneID(goNinjaID) и Delete().Where() — оба отказа обязаны прийти до всякого SQL (докажите по логу драйвера отсутствием DELETE); затем сверьте с hook.Reject из семнадцатого урока: сформулируйте, когда запрету место в схеме, а когда в рантайме. Пятое: закройте мутации Post правилом «только автор может править свой пост» — правило на privacy.ContextQueryMutationRule не годится, понадобится MutationRuleFunc с проверкой m.AuthorID() из мутации; тест: борис правит свой пост — ок, чужой — Deny. Вопрос на подумать: почему проверка политики стоит до интерсепторов на чтении и до хуков на записи, и что сломалось бы, если поменять порядок?

Личные заметкиПусто
Хранятся только в вашем браузере (ent · Privacy-политики)

Вопросы и ответы по уроку

Нужен ли privacy, если есть интерсепторы и хуки?

Да, потому что решают они разные задачи. Интерсептор фильтрует чтение: прячет строки из выборок. Хук перехватывает мутации: правит поля или отказывает. Политика — слой решений: она отвечает, допущена ли операция вообще, до SQL, до хуков и до интерсепторов, и объявлена в схеме — то есть действует для любого клиента графа. Практическая связка: политика запрещает чужие операции, интерсептор вшивает фильтры видимости, хуки — нормализация и аудит.

Privacy запрещает всё по умолчанию?

Нет — и это важно понимать честно. Если все правила вернули Skip, оценка завершается без ошибки и операция разрешается; пустой Query или Mutation-список ничего не запрещает. «Запрещено, если не разрешено» вы достраиваете сами: хвостовым privacy.AlwaysDenyRule() в списке мутаций (и чтения, если нужно). Документированные примеры ent всегда заканчивают список AlwaysDenyRule — это и есть deny по умолчанию как рабочий паттерн.

Можно ли обойти privacy-политику?

Через ent-клиент — только легальным обходом: privacy.DecisionContext(ctx, privacy.Allow) вшивает решение в контекст, и правила не оцениваются; держите этот инструмент в тестах и служебных запросах. Вне ent-клиента — как всегда с валидаторами третьего урока: рукописный SQL, другой сервис на той же базе или скрипт миграции пройдут мимо политики. Для гарантий на уровне базы существуют row-level security СУБД — это уже другая ступень ent безопасности, лежащая за пределами клиента.

Термины урока

Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.

Сравните с альтернативами

Практический вопрос после первых уроков: а почему именно эта библиотека? Мы разобрали конкурентов по одним критериям, с кодом одной задачи рядом и персональным подбором под ваши приоритеты.

Следующая ступень учебного пути

go-kit25 уроков~14 чпродвинутый25 уроков уже доступны

go-kit: микросервисный тулкит

Курс go-kit: endpoint, транспорт, логирование, метрики, трейсинг, circuit breaker, service discovery. 25 уроков с итоговым микросервисом.

  • Понимать архитектуру endpoint → transport → service
  • Оборачивать сервисы middleware: логи, метрики, трейсинг
  • Подключать HTTP и gRPC транспорты
  • Ставить rate limiting и circuit breaker