Skip to content

Latest commit

 

History

History
217 lines (152 loc) · 15.3 KB

README.md

File metadata and controls

217 lines (152 loc) · 15.3 KB

VGV FFCA Plugin

A Claude Code plugin that operationalizes Feature-First Clean Architecture (FFCA) for Flutter monorepos.

Developed with 💙 by Very Good Ventures 🦄

Overview

VGV FFCA Plugin teaches Claude the Feature-First Clean Architecture conventions and enforces its layer rules as you work. It ships three asset types:

  • Skills that guide FFCA workflows: where code lives, how to scaffold a feature, how to wire routing, how to couple features, and how to audit a repo.
  • Hooks: a blocking validation hook that runs on every pubspec.yaml edit and stops the edit when it breaks a layer dependency rule, with the rule and the fix in the message so Claude self-corrects, plus a Very Good CLI check that gates its MCP tool calls.
  • An MCP configuration that wires the Very Good CLI server for project and package operations.

The conventions themselves live in references/ffca/, a byte mirror of the canonical FFCA documentation on VGV Engineering, regenerated by scripts/sync_reference.dart. The skills never restate the conventions: they point into the mirror by file and section, so the architecture has exactly one source of truth.

The architecture reference

references/ffca/ holds one file per page of the canonical documentation, fetched verbatim from the FFCA section of VGV Engineering:

File Covers
overview.md Goals, the three-part structure, feature archetypes, shared libraries, the layer model
domain.md Models, Commands and Queries, repository interfaces, composing features, the Summary pattern
data.md Data sources, DTOs, converters and mappers
presentation.md Modules, localizations, widgets that own state, widget slots, subfeature barrels
navigation.md Callback injection, typed routes, splitting the routing table, deep links
project_structure.md Dependency rules, deferred loading, naming, folder layout, tooling, add-to-app
faq.md Callable classes, auth and user profiles, one big OpenAPI spec, nested objects

Do not edit these by hand. Fix the architecture at the source, then sync:

dart run scripts/sync_reference.dart          # rewrite the mirror
dart run scripts/sync_reference.dart --check  # fail if it is stale

A scheduled workflow runs --check weekly, so the mirror cannot quietly fall behind upstream.

The stack

This plugin is the structure layer of Very Good Ventures' AI-assisted engineering stack. It composes with the other two plugins rather than replacing them.

Layer Plugin Role
Workflow vgv-wingspan brainstorm, plan, build, review
Structure vgv-ffca-plugin Monorepo structure, layer rules, FFCA conventions
Code quality vgv-ai-flutter-plugin Bloc, testing, a11y, theming, analyze and format hooks

Each FFCA skill self-scopes to FFCA repos through its trigger description, so the plugin coexists with vgv-ai-flutter-plugin without conflicts. The detection signal is a features/ folder containing {feature}_domain, {feature}_data, or {feature}_presentation packages.

FFCA architecture vs layered architecture: which applies

Both this plugin and vgv-ai-flutter-plugin ship an architecture skill, and they target different structures. Use the signal in the repo to tell them apart:

  • This plugin's ffca-architecture applies to FFCA monorepos: a features/ folder of {feature}_domain, {feature}_data, and {feature}_presentation packages, with apps/ and shared/ alongside.
  • vgv-ai-flutter-plugin's layered-architecture applies to the standard VGV layered app: a packages/ folder of _repository and _api_client packages with business logic and presentation in the app's lib/.

The ffca-architecture skill defers to layered-architecture when it sees the packages/ + _repository/_api_client shape, and the validation hook only runs on repos that have a features/ folder, so a layered repo never triggers FFCA enforcement.

Installation

One-line install from your terminal:

claude plugin marketplace add VeryGoodOpenSource/very-good-claude-code-marketplace && claude plugin install vgv-ffca-plugin

Or inside an active Claude Code session, run these as two separate commands (the second only after the first completes):

  1. Add the marketplace:

    /plugin marketplace add VeryGoodOpenSource/very-good-claude-code-marketplace
    
  2. Install the plugin:

    /plugin install vgv-ffca-plugin
    

For more details, see the Very Good Claude Marketplace.

Local development

Load the plugin from a local checkout, validate it, and run the validator tests:

claude --plugin-dir .
claude plugin validate .
cd scripts && dart test

Skills

Skill Description
FFCA Architecture Orientation: where code lives across apps/, features/, shared/, the naming conventions, the layer dependency rules, and the anti-patterns to reject
FFCA Feature Scaffold and extend a feature: the three-package domain, data, and presentation structure, headless and presentation-only features, models, repositories, Commands and Queries, DTOs, mappers, Cubits, and Modules
FFCA Routing Navigation: callback injection, go_router_builder typed routes, splitting the routing table, deferred imports, the $extra hydration pattern, and the feature-isolation constraints
FFCA Cross-Feature Coupling features: domain-to-domain dependencies, the Summary pattern, Queries that combine repositories, sharing widgets, widget slots, and composing features
FFCA Audit Whole-repo health check: dispatches the ffca-layer-auditor agent, which runs the mechanical layer, naming, and cycle checks plus a qualitative review and returns a per-package verdict table

Skills activate automatically when Claude detects an FFCA repo or an FFCA-shaped question. You can also invoke them directly:

/ffca-architecture
/ffca-feature
/ffca-routing
/ffca-cross-feature
/ffca-audit

Hooks

A PostToolUse hook runs on every Edit, MultiEdit, or Write and ignores any other tool call. When the edited file is a pubspec.yaml inside an FFCA-shaped repo, it validates the package's layer dependencies. When it is a Dart file, it validates that file's imports and exports. A PreToolUse hook gates every Very Good CLI MCP tool call.

Hook Event Behavior
Validate layers (validate_layers.sh) PostToolUse (Edit/MultiEdit/Write) Runs the FFCA validator incrementally: on the edited package and its direct dependents for a pubspec edit (errors only for dependents), and on the edited file alone for a Dart edit. Exits 2 on an error (blocking: Claude must fix the dependency before continuing), printing the rule and the fix. Passes warnings to Claude as non-blocking context. Passes silently otherwise
Check VGV CLI (check_vgv_cli.sh) PreToolUse (mcp__.*very-good-cli__.*) Auto-approves Very Good CLI MCP tool calls when the CLI is installed at 1.3.0 or newer, so they work in every run mode. Denies with an install or upgrade message when the CLI is missing or outdated. Stands aside for any other tool, or when the CLI version cannot be read

The validator checks package naming under features/, the layer dependency rules, that nothing depends on an app, and cycles. It also checks that each feature's packages fit together: a data package depends on its own _domain under dependencies, a data package never exists without a _domain in its feature, and a _domain package never depends on the Flutter SDK. In lib/ and bin/, it checks that each feature package has a primary barrel lib/{package}.dart, that imports of other workspace packages go through a barrel at the root of their lib/, that another feature's presentation is imported through a dedicated barrel rather than its primary one, that a data barrel never exports DTOs or mappers, that every declared workspace dependency is imported, and that every imported workspace package is declared. An import of an undeclared package is checked against the same layer rules as a pubspec dependency, since a Dart workspace resolves it anyway. On a pubspec edit, a package with no lib/ yet is not required to have a barrel, so a new package can be scaffolded pubspec first.

The validator is also runnable directly for CI and audits, across the whole workspace:

dart run scripts/validate_layers.dart --all

Rules, severities, and suppressions

Every finding carries a rule ID in brackets. An error exits 2 and blocks the hook. A warning is reported but exits 0.

Rule ID Severity Finding
package-name error A package under features/{f}/ is not named {f}_domain, {f}_data, {f}_data_{backend}, or {f}_presentation
depends-on-app error Any package depends on an app
shared-depends-on-feature error A shared package depends on a feature package
presentation-depends-on-data error A presentation package depends on a data package
domain-depends-on-data error A domain package depends on a data package
domain-depends-on-presentation error A domain package depends on a presentation package
data-depends-on-presentation error A data package depends on a presentation package
data-depends-on-feature-data error A data package depends on another feature's data package
layer-dependency error Any other dependency the rules table does not allow
dependency-cycle error Packages depend on each other in a cycle
data-missing-own-domain error A data package does not depend on its own _domain under dependencies
data-without-domain error A data package's feature has no _domain package
domain-uses-flutter error A domain package depends on the Flutter SDK
presentation-missing-own-domain warning A presentation package does not depend on its feature's existing _domain
missing-primary-barrel error A feature package has no lib/{package}.dart
deep-import error A Dart file imports or exports another workspace package past the barrels at the root of its lib/
cross-feature-primary-barrel error A feature imports another feature's primary presentation barrel instead of a dedicated one
data-barrel-exports-internals error A data package's primary barrel exports from dtos/ or mappers/. Data sources may be exported, since the app constructs them
undeclared-dependency error A Dart file imports a workspace package its pubspec does not declare under dependencies. When the layer rules forbid that dependency, the layer rule's ID is reported instead
unused-dependency warning A package declares a workspace dependency under dependencies but never imports it in lib/ or bin/
suppression-missing-reason error An ffca-ignore comment has no reason
suppression-unknown-rule error An ffca-ignore comment names a rule that does not exist or cannot be suppressed, or a // comment in a Dart file names a rule reported on the pubspec
unused-suppression warning An ffca-ignore comment matches no finding (--all only)

To accept a finding as a deliberate exception, add a comment with the rule ID and a reason to the pubspec of the package it is reported on:

# ffca-ignore: data-missing-own-domain — legacy repository, moving to orders_domain in #123
name: orders_data

A finding in a Dart file can also be suppressed in that file, with //:

// ffca-ignore: deep-import — the model is not exported from orders_domain yet
import 'package:orders_domain/models/order.dart';

The separator after the rule ID can be an em dash, an en dash, a hyphen, or a colon. A suppression in a pubspec covers every finding with that rule ID in that package, and one in a Dart file covers that file. Rules reported on the pubspec, such as unused-dependency or missing-primary-barrel, can only be suppressed in the pubspec. The reason is required, so each exception explains itself in review. The last three rules cannot be suppressed. --all reports how many findings were suppressed, and flags suppressions that no longer match anything so they can be removed.

Prerequisites

  • Dart SDK must be available on your PATH. The validator imports only dart:io, so it runs with just the SDK, no dart pub get required.
  • jq is used to parse the hook payload. The hooks are skipped gracefully if jq is not installed, and the validation hook also if dart is not installed.

Run the hook tests with:

bash hooks/check_vgv_cli_test.sh
bash hooks/validate_layers_test.sh

MCP Integration

The plugin's .mcp.json connects Claude Code to the Very Good CLI MCP server (very_good mcp) under the server name very-good-cli, the same name vgv-ai-flutter-plugin uses. The ffca-feature skill calls it to scaffold and resolve each feature package.

Tool What it does
create Scaffold packages from templates. FFCA uses dart_package for domain and data, flutter_package for presentation
packages_get Get dependencies for a single package or recursively across the monorepo
test Run tests with coverage enforcement
packages_check_licenses Audit dependency licenses against an allowed list

When installed from a marketplace, Claude Code names these tools mcp__plugin_vgv-ffca-plugin_very-good-cli__<tool>, and the skills' allowed-tools use that full name.

The plugin does not bundle the Dart MCP server (dart mcp-server). No FFCA skill calls it, and vgv-ai-flutter-plugin already provides it for analyze and format.

The server needs Very Good CLI 1.3.0 or newer. Install it with dart pub global activate very_good_cli.

Agent

Agent Behavior
ffca-layer-auditor Read-only architecture auditor. Runs the validator in --all mode, then adds source-level checks (barrel scope, DTO leakage, Command/Query necessity, module entry, split routing tables, deferred-loading reachability, misplaced packages, high fan-in) and returns a per-package verdict table. Reports violations, never auto-fixes

The auditor runs in its own context, so the same architecture review can be dispatched from the ffca-audit skill, a refactor, or a pre-PR flow without crowding the main conversation. The per-edit hook prevents bad pubspec dependencies as they are written; the agent answers whether the whole repo is healthy on demand.

How it fits together

The hook keeps individual pubspec edits compliant. The skills teach the conventions and workflows. The ffca-layer-auditor agent answers whether the whole repo is healthy, on demand and reusable across flows. All of them read from the same references/ffca/ mirror, so when the architecture evolves, a sync is the only change.