Skip to content

Определение Federation

Для обычной Federation используйте EndgeFederation.define(...). Declaration описывает identity, Module factories, дочерние Federations и ordering; общий lifecycle и типизированный доступ к узлам предоставляет framework.

Module definitions

ts
import type { EndgeModuleDefinition } from '@endge/core'
import { EndgeFederation } from '@endge/core'

const FEATURE_MODULES = [
  {
    key: 'projectRuntime',
    create: () => new ProjectRuntime_Module(),
  },
  {
    key: 'schedule',
    create: ({ getModule }) => new Schedule_Module(
      getModule<ProjectRuntime_Module>('projectRuntime'),
    ),
    after: 'projectRuntime',
  },
] as const satisfies readonly EndgeModuleDefinition[]

Каждый Module создаётся лениво при первой конфигурации Federation. Factory getModule(key) возвращает тот же экземпляр зависимости, который будет доступен через Federation. Framework отклоняет неизвестные keys, циклическое создание и повторное объявление одного key.

before и after принимают один key либо список keys и задают lifecycle-ordering. Dependency в constructor и lifecycle-ordering — разные contracts: если Module получает другой Module через getModule, необходимый порядок фаз всё равно следует объявить явно.

Простая Federation

ts
export const Feature = EndgeFederation.define({
  id: 'feature',
  name: 'Feature',
  modules: FEATURE_MODULES,
})

Literal keys и return types factories формируют типизированные readonly accessors:

ts
Feature.projectRuntime
Feature.schedule

await Feature.boot(context)
await Feature.reset()

Если concrete Module объявляет собственный lifecycle context через generic EndgeModule<FeatureContext>, define(...) выводит тот же context для Feature.boot(context) и Feature.build(context). Пустой wrapper-class только ради сужения типа boot не требуется.

Ручные однотипные configureFederation() и getters вида getModule<T>('key') для каждого Module не нужны.

Child Federation definitions

Дочерняя Federation объявляется в отдельном federations массиве. Её key участвует в том же локальном ordering namespace, что и keys Modules:

ts
const Workspace = EndgeFederation.define({
  id: 'workspace',
  modules: WORKSPACE_MODULES,
})

export const Application = EndgeFederation.define({
  id: 'application',
  modules: APPLICATION_MODULES,
  federations: [
    {
      key: 'workspace',
      federation: Workspace,
      after: 'configuration',
    },
  ],
})

Application.workspace имеет точный тип facade Workspace. После объявления parent управляет lifecycle child, поэтому приложение вызывает boot, build и reset только у Application.

Federation с дополнительной логикой

Если application или package владеет дополнительной orchestration, наследуйте результат define(...):

ts
const ApplicationFederation = EndgeFederation.define({
  id: 'application',
  name: 'Application',
  modules: APPLICATION_MODULES,
})

export class Application extends ApplicationFederation {
  public static async init(context: ApplicationContext): Promise<void> {
    await this.boot(context)
    await this.preferences.apply()
  }
}

В custom class остаётся только поведение конкретного owner. Generated accessors, module graph, тип lifecycle context и базовый lifecycle не переопределяются.

Прямое наследование от EndgeFederation оправдано только для действительно нестандартного configuration algorithm или для framework contract tests.

Выбор id

Используйте короткую стабильную identity, связанную с владельцем Federation:

ts
EndgeFederation.define({
  id: 'workspace',
  name: 'Workspace',
  modules: WORKSPACE_MODULES,
})

Не генерируйте id во время запуска и не используйте разные module graphs с одним id. В одном JavaScript realm такой id всегда разрешается в один общий runtime-host.