Это руководство помогает преобразовать пути репозитория и зависимости в архитектурное правило. Оно описывает модель компонентов, YAML DSL, выбор контракта, возможности языков и наборы правил. Нужны GoCPG с подходящим языковым адаптером и рабочая копия проекта, пути которой можно классифицировать. Ниже приведён полный пример правила Python: сохраните его в рабочей копии проекта.
Проверьте, что gocpg architecture --help перечисляет validate, describe и explain. Если команда неизвестна, установленный бинарник не поддерживает это руководство. Установите подходящую сборку перед продолжением.
После проверки правила прочитайте выполнение и управление. Для изучения сохранённых результатов используйте интерфейсы и результаты. Руководство по внедрению связывает эти шаги в единый процесс работы с репозиторием.
Модель архитектуры
Модель назначает каждой учитываемой сущности один устойчивый UID компонента. id — читаемое имя, которое можно изменить. uid сохраняет идентичность при переименовании или переносе пути. Если ответственность компонента изменилась, назначьте новый UID. Родительские компоненты объединяют дочерние; селекторы конечных компонентов классифицируют сущности. source_roots разделяют промышленный, тестовый, сгенерированный код и материалы измерений производительности. Теги layer, scope и runtime задают независимые измерения. Например, компонент может одновременно иметь layer: application и scope: billing.
Для блокирующей проверки классификация должна быть полной и однозначной. Покрытие рассчитывается по известному множеству учитываемых сущностей; пустую выборку обрабатывает политика модели. Политика модели определяет обработку неклассифицированных сущностей, конфликтов селекторов, сгенерированного кода, пустой выборки и минимального покрытия. Если два конечных компонента выбирают одну сущность, явно измените селекторы или политику классификации. В примере conflict_policy: error превращает пересечение селекторов в ошибку классификации.
В примере src/ui/** и src/storage/** образуют отдельные конечные компоненты. Политика предполагает, что в тестовом проекте это единственные каталоги промышленного кода. Для реального проекта с src/jobs/ нужен третий компонент или явное решение об обработке этого каталога.
Язык архитектурных правил
Архитектурное правило задаёт spec_version: "2.0", kind: architecture, устойчивый id и раздел architecture. Неизвестные поля и неподдерживаемые версии вызывают ошибку. Если kind отсутствует, документ передаётся прежнему компилятору правил поиска шаблонов. Каждый документ содержит одну модель компонентов и один контракт. Общие параметры контрактов: селекторы, relation, languages, required_capabilities и minimum_coverage. У каждого контракта есть дополнительные поля.
Сохраните полный пример ниже в gocpg/docs/examples/architecture-control/architecture.yaml относительно рабочего каталога. При необходимости создайте родительские каталоги. В исходной рабочей копии CodeGraph этот файл уже поставляется. Команды компиляции читают только YAML: CPG и файлы Python для них не нужны.
spec_version: "2.0"
kind: architecture
id: example.ui-no-storage
version: "1"
fingerprint_version: 1
languages: [python]
architecture:
source_roots:
- path: src
source_set: production
classification:
exhaustive: true
conflict_policy: error
unclassified_policy: error
generated_policy: ignore
minimum_coverage: 1.0
components:
- uid: cmp-example-ui
id: ui
name: UI
kind: leaf
match:
paths: ["src/ui/**"]
- uid: cmp-example-storage
id: storage
name: Storage
kind: leaf
match:
paths: ["src/storage/**"]
contract: forbidden_dependency
from:
components: [cmp-example-ui]
to:
components: [cmp-example-storage]
relation:
edge_types: [import]
reachability: direct
languages: [python]
required_capabilities: [imports, source_sets]
minimum_coverage: 1.0
Тип ребра import обозначает отношение графа. Возможность imports подтверждает, что производитель фактов Python может предоставить это отношение. Замена reachability: direct на транзитивную проверку изменяет смысл правила и может сделать прежние решения управления неприменимыми.
Из корня репозитория в PowerShell, с gocpg в PATH, проверьте выбранный и невыбранный языки:
gocpg architecture validate --file gocpg/docs/examples/architecture-control/architecture.yaml --language python --json
gocpg architecture validate --file gocpg/docs/examples/architecture-control/architecture.yaml --language c --json
gocpg architecture describe --file gocpg/docs/examples/architecture-control/architecture.yaml --component ui --json
Для Python ожидаются rule_id: example.ui-no-storage, applicable: true и gate_evaluated: false. Для C ожидаются applicable: false и language_not_selected, хотя команда может завершиться с нулевым кодом. describe должен определить cmp-example-ui. Эти команды компилируют и описывают модель. Следующий шаг — выполнить архитектурную проверку по CPG проекта и получить результат для решения о выпуске.
В исходниках под этой моделью импорт из src/ui/view.py в src/storage/db.py нарушает контракт прямой зависимости. Модуль UI без импорта хранилища не создаёт такую находку. Правило устанавливает результат только после полного архитектурного анализа CPG той же ревизии исходников. Для проверки этих импортов используйте выполнение архитектурных проверок.
Каталог контрактов
Выберите контракт по вопросу, на который должен ответить граф. В v1 ровно 12 контрактов:
| Контракт | Вопрос | Существенное условие |
|---|---|---|
forbidden_dependency |
Должен ли A избегать B? | Явно выберите прямое или транзитивное отношение. |
allowed_dependencies |
Может ли A зависеть только от перечисленных компонентов? | Для выбранных рёбер список разрешений закрыт. |
required_dependency |
Должен ли каждый A зависеть от B? | Выберите each_subject, each_component, at_least_one или exactly_one. |
layers |
Какой слой может вызывать другой? | Задайте каждый слой и его набор may_depend_on. |
independence |
Должны ли группы оставаться независимыми? | Группируйте по компоненту или измерению тегов. |
acyclic |
Есть ли цикл в графе компонентов? | Определите обработку петель на самом компоненте. |
public_api_only |
Должны ли вызывающие использовать экспортируемый API? | Определите публичный интерфейс целевого компонента. |
protected_component |
Кто может обращаться к внутреннему компоненту? | Перечислите разрешённых потребителей. |
exclusive_api |
Кто может использовать именованный API? | Выберите один API и его потребителей. |
exhaustive_classification |
Каждая ли учитываемая сущность классифицирована ровно один раз? | Неизвестный знаменатель или конфликт означает неполноту. |
metric_threshold |
Превышает ли метрика компонента предел? | Укажите метрику, оператор и значение. |
resolution_coverage |
Достаточно ли разрешены зависимости? | Проверяйте каждый язык и каждую возможность отдельно. |
В примере forbidden_dependency запрещает прямой импорт из UI в хранилище. Для разрешённого импорта из UI в слой приложения нужны другая модель и контракт. Селектор без совпадений обрабатывается по политике пустой выборки. Настройте эту политику под ожидаемый состав компонентов проекта.
Реестр возможностей
В исходной рабочей копии CodeGraph функция FoundationCapabilityManifests в gocpg/pkg/architecture/graph.go задаёт семейства фактов по языкам. Текущая квалификация v1 охватывает C, C++, C#, Go, Java, JavaScript, Kotlin, PHP, Python, TypeScript и 1C независимо друг от друга. Семейства включают includes, imports, calls, inheritance, implements, annotations, type_references, source_sets, а также динамические импорты и импорты только типов, когда адаптер языка предоставляет их. Реестр языковых пресетов содержит одиннадцать пакетов; квалификация выполняет 264 корректных и ошибочных примера для двенадцати контрактов.
complete означает доступность всех требуемых возможностей. degraded означает отсутствие необязательного семейства. insufficient означает отсутствие обязательного семейства. Для блокирующей проверки нужен complete по каждому выбранному языку и каждой требуемой возможности. Реестр возможностей связывает языковой адаптер с семействами фактов, которые использует контракт.
Создание набора правил
Языковой набор правил требует одного языка, точной семантической версии, манифеста возможностей и корректных и ошибочных тестовых исходников для каждого контракта. Запись тестового примера связывает путь и SHA-256 исходника с ожидаемыми фактами и результатом вычисления. Квалификация отклоняет отсутствующие пути, выход за разрешённый каталог, символические ссылки, несовпадающие хеши, псевдонимы другого языка и неожиданные результаты. Квалификация читает исходники примеров, проверяет их хеши и сравнивает фактический результат с ожидаемым.
Для изменения поставляемого набора нужна исходная рабочая копия CodeGraph и Go 1.27, указанный в gocpg/go.mod. Реестр v1 загружает 14 пакетов: три общих и одиннадцать языковых. Ниже полный манифест gocpg/configs/architecture/presets/v1/language/python/manifest.yaml:
id: python/module-boundaries
version: 1.0.0
languages: [python]
required_capabilities: [imports, source_sets]
fixture_profile: v1-contract-pairs
fixture_catalog: fixtures.yaml
fixture_catalog задаёт каталог примеров относительно манифеста. В нём для каждого из 12 контрактов перечислены корректные и ошибочные исходники, их SHA-256 и facts_profile. Из корня модуля gocpg прочитайте поставляемые файлы:
Get-Content configs/architecture/presets/v1/language/python/fixtures.yaml
Get-Content configs/architecture/presets/v1/language/python/fixtures/valid.py
Get-Content configs/architecture/presets/v1/language/python/fixtures/invalid.py
В корректном исходнике импорт идёт из fixture_public.api, в ошибочном — из fixture_internal.private_api. Пути source в каталоге отсчитываются от каталога fixtures.yaml. При изменении исходников обновите их хеши и ожидаемые факты.
Квалификация заменяет окончания строк CRLF на LF перед расчётом SHA-256. Из корня модуля gocpg вычислите хеш тем же способом:
$source = [IO.File]::ReadAllText("configs/architecture/presets/v1/language/python/fixtures/valid.py")
$bytes = [Text.Encoding]::UTF8.GetBytes($source.Replace("`r`n", "`n"))
$sha = [Security.Cryptography.SHA256]::Create()
($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString("x2") }) -join ""
$sha.Dispose()
Для поставляемого valid.py ожидается 2b84c4226bd23ab9625636d21826036d2fc2a3e3f0df63b010c4794293f38ec1. После изменения файла запишите новый хеш во все соответствующие поля source_sha256 в fixtures.yaml.
При подготовке изменения набора:
- Сохраните его зарегистрированный
id, задайте точную версию и объявите один язык. Состав пакетов v1 определяетLoadV1PresetRegistry; расширение состава включает изменение этого реестра и его проверок. - Включите только возможности, подтверждённые для адаптера и профиля обогащения.
- Добавьте корректные и ошибочные исходники для каждого контракта.
- Запишите хеши, ожидаемые факты и результаты.
- Из корня модуля
gocpgзапустите проверку исходников примеров и проверку разрешения точных версий:
go test ./pkg/architecture -run '^TestV1LanguagePresetsUseRealSourceBackedFixtureCases$' -count=1
go test ./pkg/architecture -run '^TestStory1243PresetResolutionRejectsLatestAndCrossLanguageAliases$' -count=1
Обе команды должны завершиться с ok. Первая загружает поставляемый реестр и проверяет пути, хеши, факты и результаты примеров. Вторая проверяет разрешение версии 1.0.0 и отклонение latest и псевдонимов между языками. При изменении набора исправьте ошибки до его публикации.
Общие шаблоны задают форму модели. Селекторы и языковые правила предоставляет проект. Каждый языковой набор проходит квалификацию на своих исходниках и фактах.
Каталог наборов правил
Три общих шаблона: generic/clean-architecture@1.0.0, generic/layered@1.0.0 и generic/modular-monolith@1.0.0. Независимые языковые манифесты находятся в gocpg/configs/architecture/presets/v1/language/ для одиннадцати квалифицированных языков выше. Используйте точный id@version: latest и псевдонимы между языками отклоняются. Перед выбором набора проверьте его текущие возможности и тестовые примеры в манифестах.
Проверки реализации
Команды ниже запускают проверки из корня модуля gocpg. Они проверяют реализацию модели, каталог контрактов, возможности языков и пресеты. Для проверки своего репозитория выполните архитектурный анализ.
go test ./pkg/architecture -run '^TestComponentHierarchyStableUIDAndDuckDBPersistence$' -count=1
go test ./pkg/architecture -run '^TestStory1240V1ContractRegistry$' -count=1
go test ./pkg/architecture -run '^TestV1FoundationCapabilitiesAndPresetIDsMatchNormativeSource$' -count=1
go test ./pkg/architecture -run '^TestV1LanguagePresetsUseRealSourceBackedFixtureCases$' -count=1
go test ./pkg/architecture -run '^TestStory1243V1PresetRegistryIsExactAndComplete$' -count=1