6. Фасеты
Модуль с двадцатью интерфейсами в плоском списке нечитаем. Фасет — это именованная группировка интерфейсов (и вложенных фасетов) внутри тела модуля. Это исключительно средство организации: у фасетов нет семантики развёртывания, нет стабильных идентификаторов, нет присутствия за пределами модуля, в котором они живут.
module Orders { team: Commerce
facet OrdersResource { "Order CRUD operations" base: "/orders"
interface Create interface Get interface Update interface Delete
facet Products { base: "/products" interface Add interface Remove } }
facet Webhooks { interface Subscribe interface Unsubscribe }}Два фасета верхнего уровня на Orders: OrdersResource (со своим вложенным фасетом Products) и Webhooks. Каждый группирует интерфейсы под именем; каждый может нести поля, метки и описание.
Зачем нужны фасеты
Без фасетов каждый интерфейс живёт прямо в теле модуля. Это работает для небольших модулей. Для модулей с реальной публичной поверхностью — HTTP-сервис с двадцатью эндпоинтами, доменный сервис, группирующий операции по агрегату — плоская раскладка превращается в шум.
Фасеты дают вам место для того, чтобы:
- Сгруппировать концептуально связанные интерфейсы (операции
/ordersвместе). - Прикрепить общие поля, например базовый путь, который должен применяться ко всему, что находится ниже.
- Применить шаблон типа (Глава 16), чтобы можно было штамповать фасет
crud, получающий фиксированный набор операций бесплатно.
Они не про развёртывание, владение или адресацию. Это относится к модулям.
Вложенность односторонняя
Правило. Модули содержат фасеты; фасеты не содержат модулей.
Фасет может содержать другие фасеты и интерфейсы. Он не может содержать модуль. Если вам нужен подмодуль внутри сервиса, объявите его как вложенный модуль (Глава 4), а не как фасет:
module Orders { facet API { interface Create // ✅ интерфейсы внутри фасета }
module Worker { // ✅ вложенный модуль — НЕ внутри фасета interface Process }
// ❌ Это было бы ошибкой разбора: // facet Bad { // module Inside { ... } // }}Причина — идентичность. Модули несут стабильные идентификаторы и представляют архитектурные элементы; фасеты организационны. Разрешение модулей внутри фасетов смешало бы эти роли.
Точечные пути
Шаги процессов и перекрёстные ссылки разрешаются через фасеты по точечным путям:
process Buy { Customer > Orders.OrdersResource.Create Customer > Orders.OrdersResource.Products.Add Customer > Orders.Webhooks.Subscribe}Полный путь — Module.Facet.[Facet.]Interface. Фасеты прозрачны для резолвера — они организуют исходный текст, но не вводят отдельное пространство имён, которое нужно было бы обходить.
Виды фасетов
facet — единственный вид фасета, определённый в стандартной библиотеке. Вы можете объявлять собственные виды фасетов, когда предпочтительна доменно-специфическая лексика:
type facet resource { "An HTTP resource — appends to a base path" append base}
module Orders { resource OrdersResource { base: "/orders" interface Post interface Get }}resource теперь — определённый пользователем вид фасета, который ведёт себя как facet плюс поле append base. (Глава 16 рассказывает об определении видов; Глава 18 — про append.)
Распространённые соглашения, которые вы встретите в реальных проектах:
resource— в стиле HTTP-ресурса, дописывает пути.capability— группировка по возможностям, без семантики путей.endpoint_group— группировка связанных интерфейсов под общей меткой.
Ни один из них не поставляется со стандартной библиотекой; команды определяют их по необходимости.
Поля, метки, описания
Тела фасетов несут то же содержимое из полей и меток, что и модули:
facet OrdersResource { "Order CRUD operations" base: "/orders" version: v2
labels { domain: Orders }
interface Create interface Get}Метки, объявленные на фасете, каскадируются на его вложенные интерфейсы и вложенные фасеты — именно так labels { domain: Orders } на фасете распространяется на каждую операцию внутри без повторов. Глава 18 полностью покрывает каскад.
Нет стабильных идентификаторов
У фасетов, как и у интерфейсов, нет стабильных идентификаторов. Их идентичность — это точечный путь внутри объемлющего модуля. Обнаружение переименования использует структурные эвристики, как и у интерфейсов (Глава 13).
Пустые тела
Фасет, которому нечего добавить сверх шаблона его типа, может опустить тело:
module Orders { resource OrdersResource // facet using a 'resource' kind, no instance additions}Это объявляет фасет OrdersResource, содержимое которого целиком приходит из типа resource. Мы встретим типы в Главе 15.
Итоги
- Фасет группирует интерфейсы (и вложенные фасеты) внутри тела модуля.
- Фасеты организационны: нет стабильных идентификаторов, нет семантики развёртывания.
- Вложенность односторонняя — модули содержат фасеты, никогда наоборот.
- Шаги процессов достигают интерфейсов внутри фасетов по точечному пути (
Module.Facet.Interface). - Пользовательские виды фасетов (
resource,capability) позволяют согласовать запись с доменной лексикой.
Что дальше
Глава 7: Процессы → — как описывается поведение и откуда на самом деле берутся стрелки зависимостей.