# Architecture Overview

Entity Builder implements two core architectural principles:

1. **EbDefinition-Centric Workflow** - All entry points save data to a configuration entity before execution
2. **Field-Centric Architecture** - Each field is the single source of truth for its configuration

## Design Principles

### 1. Field-Centric Architecture

The YAML format and UI are built around the concept that **each field knows its own configuration**:

```yaml
field_definitions:
  - entity_type: node
    bundle: article
    field_name: field_body
    field_type: text_long
    label: Body
    required: true
    # Display configuration for default mode
    widget: text_textarea
    widget_settings:
      rows: 10
    formatter: text_default
    # Field group assignment
    form_group: group_content
    view_group: group_main
    weight: 0
```

**Key Principles:**

| Principle | Description |
|-----------|-------------|
| **Single Source of Truth** | Fields define their own widget, formatter, and group assignments |
| **Default Mode Focus** | `field_definitions` configure the `default` display mode |
| **Non-Default Modes** | Use `display_field_definitions` only for teaser, card, or custom view modes |
| **Extension Integration** | Extensions add properties to existing definitions (e.g., `form_group`, `view_group`) |

### 2. EbDefinition-Centric Workflow

All entry points first save data to the `EbDefinition` config entity, then the apply workflow reads from it:

```mermaid
flowchart TB
    subgraph Phase1["Phase 1: Data Entry"]
        UI[Entity Builder UI<br/>AG Grid]
        YAML[YAML Import<br/>drush eb:import]

        UI -->|Grid Data JSON| Save[Save to Definition]
        YAML -->|File Content| Parse[YamlParser]
        Parse --> Validate[Validate]
        Validate --> Save

        Save --> DEF[(EbDefinition<br/>Config Entity)]
    end

    subgraph Phase2["Phase 2: Apply Workflow"]
        DEF -->|User clicks Apply| ODB[OperationDataBuilder]
        ODB --> DR[DependencyResolver<br/>Topological Sort]
        DR --> CD[ChangeDetector<br/>Sync Mode]
        CD --> VM[ValidationManager<br/>Two-Stage]
        VM --> OP[OperationProcessor]
        OP --> RM[RollbackManager]
        RM --> LOG[EbLog + Watchdog]
    end

    OP -->|Creates| Drupal[(Drupal Entities<br/>Bundles, Fields, Displays)]
```

## High-Level System Architecture

```mermaid
flowchart TB
    subgraph Input["Input Layer"]
        UI[Web UI<br/>AG Grid]
        CLI[Drush CLI]
        API[PHP API]
    end

    subgraph Core["Core Services"]
        ODB[OperationDataBuilder]
        OB[OperationBuilder]
        DR[DependencyResolver]
        CD[ChangeDetector]
        VM[ValidationManager]
        PG[PreviewGenerator]
        OP[OperationProcessor]
    end

    subgraph Storage["Storage Layer"]
        DEF[(EbDefinition<br/>Config Entity)]
        RB[(EbRollback<br/>Content Entity)]
        LOG[(EbLog<br/>Content Entity)]
        WD[(Watchdog)]
    end

    subgraph Extensions["Extension Plugins"]
        FG[eb_field_group]
        PA[eb_pathauto]
        AEL[eb_auto_entitylabel]
    end

    UI --> ODB
    CLI --> ODB
    API --> ODB

    ODB --> DEF
    ODB --> OB
    OB --> DR
    DR --> CD
    CD --> VM
    VM --> PG
    VM --> OP

    OP --> RB
    OP --> LOG
    LOG --> WD

    Extensions -.->|buildOperations| ODB
    Extensions -.->|getOperationDependencies| DR
    Extensions -.->|detectChanges| CD
```

## Data Flow Pipeline

```mermaid
sequenceDiagram
    participant User
    participant UI as Entity Builder UI
    participant DEF as EbDefinition
    participant ODB as OperationDataBuilder
    participant DR as DependencyResolver
    participant CD as ChangeDetector
    participant VM as ValidationManager
    participant OP as OperationProcessor
    participant Drupal as Drupal Entities

    User->>UI: Edit definition
    UI->>DEF: Save grid data
    User->>UI: Click Apply
    UI->>ODB: Build operations
    ODB->>DR: Resolve dependencies
    DR->>CD: Detect changes
    CD->>VM: Validate batch
    VM->>OP: Execute operations
    OP->>Drupal: Create/Update entities
    OP-->>User: Success + Rollback available
```

## Definition Keys

### Core Definition Keys

| Key | Purpose | UI Tab | Operations Generated |
|-----|---------|--------|---------------------|
| `bundle_definitions` | Content types, vocabularies, media types | Bundles | `create_bundle` |
| `field_definitions` | Fields with default mode display config | Fields | `create_field`, `configure_form_mode`, `configure_view_mode` |
| `display_field_definitions` | Non-default display mode overrides | - | `configure_form_mode`, `configure_view_mode` |
| `menu_definitions` | Custom menus | - | `create_menu` |

### Extension Definition Keys

| Extension | Key | UI Tab | Operations Generated |
|-----------|-----|--------|---------------------|
| `eb_field_group` | `field_group_definitions` | Field Groups | `create_field_group` |
| `eb_pathauto` | (bundle column: `pathauto_pattern`) | Bundles | `create_pathauto_pattern` |
| `eb_auto_entitylabel` | (bundle columns: `auto_entitylabel_*`) | Bundles | `configure_auto_entitylabel` |

## Field Definition Structure

Each field definition contains everything needed to configure that field:

```mermaid
erDiagram
    FIELD_DEFINITION {
        string entity_type "node, taxonomy_term, media"
        string bundle "Target bundle"
        string field_name "field_* machine name"
        string field_type "text_long, entity_reference, etc"
        string label "Human-readable label"
        boolean required "Is field required"
        int cardinality "Number of values"
    }

    FIELD_DEFINITION ||--o| STORAGE_SETTINGS : has
    STORAGE_SETTINGS {
        string target_type "For entity_reference"
        int max_length "For string fields"
        int precision "For decimal fields"
    }

    FIELD_DEFINITION ||--o| CONFIG_SETTINGS : has
    CONFIG_SETTINGS {
        object handler_settings "For entity_reference"
        string min "For number fields"
        string max "For number fields"
    }

    FIELD_DEFINITION ||--o| FORM_DISPLAY : has
    FORM_DISPLAY {
        string widget "Widget plugin ID"
        object widget_settings "Widget configuration"
        string form_group "Field group assignment"
        int weight "Display order"
    }

    FIELD_DEFINITION ||--o| VIEW_DISPLAY : has
    VIEW_DISPLAY {
        string formatter "Formatter plugin ID"
        object formatter_settings "Formatter configuration"
        string label_display "above, inline, hidden"
        string view_group "Field group assignment"
    }
```

**Example YAML:**

```yaml
field_definitions:
  - # Identity
    entity_type: node
    bundle: article
    field_name: field_category

    # Field Type
    field_type: entity_reference
    label: Category
    description: Select a category for this article.
    required: true
    cardinality: 1

    # Storage Settings (shared across all instances)
    field_storage_settings:
      target_type: taxonomy_term

    # Instance Settings (specific to this bundle)
    field_config_settings:
      handler_settings:
        target_bundles:
          categories: categories

    # Form Display (default mode)
    widget: options_select
    widget_settings: {}

    # View Display (default mode)
    formatter: entity_reference_label
    formatter_settings:
      link: true
    label_display: above

    # Field Group Assignment (eb_field_group)
    form_group: group_classification
    view_group: group_meta

    # Weight (display order)
    weight: 5
```

### Why Field-Centric?

1. **Maps to UI**: Each row in the Fields grid contains all field configuration
2. **Spreadsheet Export**: Flat structure exports cleanly to Excel/Google Sheets
3. **Single Edit Point**: Change widget/formatter/group in one place
4. **Reduces Redundancy**: No separate display_field_definitions for default mode

## Flat Operations Pattern

All definition data is converted to flat operation arrays before processing:

```mermaid
flowchart LR
    subgraph Input["Definition Format"]
        BD[bundle_definitions]
        FD[field_definitions]
        FGD[field_group_definitions]
        DFD[display_field_definitions]
    end

    subgraph Builder["OperationDataBuilder"]
        B1[buildBundleOperations]
        B2[buildFieldOperations]
        B3[buildDisplayOperations]
        EXT[extension.buildOperations]
    end

    subgraph Output["Flat Operations"]
        O1["create_bundle"]
        O2["create_field"]
        O3["configure_form_mode"]
        O4["configure_view_mode"]
        O5["create_field_group"]
    end

    BD --> B1 --> O1
    FD --> B2 --> O2
    FD --> B3 --> O3
    FD --> B3 --> O4
    FGD --> EXT --> O5
    DFD --> B3
```

**Input (Definition Format):**
```yaml
bundle_definitions:
  - entity_type: node
    bundle_id: article
    label: Article

field_definitions:
  - entity_type: node
    bundle: article
    field_name: field_body
    field_type: text_long
    widget: text_textarea
```

**Output (Operation Format):**
```php
[
    ['operation' => 'create_bundle', 'entity_type' => 'node', 'bundle_id' => 'article', ...],
    ['operation' => 'create_field', 'entity_type' => 'node', 'bundle' => 'article', ...],
    ['operation' => 'configure_form_mode', 'widget' => 'text_textarea', ...],
    ['operation' => 'configure_view_mode', 'formatter' => 'text_default', ...],
]
```

### Benefits of Flat Operations

| Benefit | Description |
|---------|-------------|
| **Unified Processing** | All operations go through the same pipeline |
| **Explicit Dependencies** | Dependencies calculated from operation data, not nesting |
| **Extension Simplicity** | Extensions just return operation arrays |
| **Batch Context** | Validators see what will be created in the same batch |
| **Testability** | Operations are plain arrays easy to test |

## Dependency Resolution

Dependencies are automatically detected and operations ordered using Kahn's topological sort:

```mermaid
flowchart TD
    subgraph "Dependency Graph Example"
        CB1[create_bundle<br/>taxonomy_term:categories]
        CB2[create_bundle<br/>node:article]

        CF1[create_field<br/>field_category]
        CF2[create_field<br/>field_body]

        CFM1[configure_form_mode<br/>field_category]
        CFM2[configure_form_mode<br/>field_body]

        CVM1[configure_view_mode<br/>field_category]
        CVM2[configure_view_mode<br/>field_body]

        CFG[create_field_group<br/>group_content]

        CB1 --> CF1
        CB2 --> CF1
        CB2 --> CF2

        CF1 --> CFM1
        CF1 --> CVM1
        CF2 --> CFM2
        CF2 --> CVM2

        CF1 --> CFG
        CF2 --> CFG
    end
```

| Operation | Depends On |
|-----------|------------|
| `create_field` | `create_bundle` (for the field's bundle) |
| `create_field` (entity_reference) | Target bundle |
| `configure_form_mode` | `create_field` |
| `configure_view_mode` | `create_field` |
| `create_field_group` | Bundle, display, child fields, parent group |
| `create_pathauto_pattern` | `create_bundle` |

## Smart Sync Mode

Change detection compares definitions with current Drupal state:

```mermaid
flowchart TD
    Start[Operation Data] --> Exists{Entity<br/>Exists?}

    Exists -->|No| Create[create_* operation]
    Exists -->|Yes| Changed{Has<br/>Changes?}

    Changed -->|Yes| Update[update_* operation]
    Changed -->|No| Skip[Skip operation]

    Create --> Execute[Execute]
    Update --> Execute
    Skip --> Next[Next Operation]
```

| Entity Exists | Has Changes | Result |
|---------------|-------------|--------|
| No | N/A | `create_*` |
| Yes | Yes | `update_*` |
| Yes | No | skip |

## Two-Stage Validation

```mermaid
flowchart TB
    subgraph Stage1["Stage 1: Operation Validation"]
        O1[CreateBundleOperation<br/>validate entity type]
        O2[CreateFieldOperation<br/>validate field type]
        O3[ConfigureFormModeOperation<br/>validate widget]
    end

    subgraph Stage2["Stage 2: Validator Plugins"]
        V1[DependencyValidator<br/>entity/bundle/field existence]
        V2[WidgetCompatibilityValidator<br/>widget supports field type]
        V3[FormatterCompatibilityValidator<br/>formatter supports field type]
        V4[CircularDependencyValidator<br/>no circular dependencies]
        V5[RequiredFieldsValidator<br/>required properties present]
    end

    Operations[Operations Array] --> Stage1
    Stage1 --> Stage2
    Stage2 --> Result{Valid?}
    Result -->|Yes| Execute[Execute Operations]
    Result -->|No| Errors[Return Errors]
```

1. **Stage 1**: Operations validate their specific requirements
   - `CreateFieldOperation`: Validate field type exists
   - `CreateBundleOperation`: Validate entity type supports bundles

2. **Stage 2**: Validator plugins check cross-cutting concerns
   - `DependencyValidator`: Entity/bundle/field existence
   - `WidgetCompatibilityValidator`: Widget supports field type
   - `CircularDependencyValidator`: No circular dependencies

## Entity Types

```mermaid
erDiagram
    EbDefinition ||--o{ EbRollback : "has rollbacks"
    EbDefinition ||--o{ EbLog : "has logs"
    EbRollback ||--|{ EbRollbackOperation : "contains"

    EbDefinition {
        string id PK "Machine name"
        string label "Human-readable"
        array bundle_definitions "Bundle configs"
        array field_definitions "Field configs"
        array field_group_definitions "Group configs"
        string application_status "applied, pending"
    }

    EbRollback {
        int id PK "Auto-increment"
        string definition_id FK "Source definition"
        string status "pending, completed, failed"
        int operation_count "Number of operations"
        timestamp created "When apply started"
    }

    EbRollbackOperation {
        int id PK "Auto-increment"
        int rollback_id FK "Parent rollback"
        string operation_type "Plugin ID"
        map original_data "Serialized undo data"
        int sequence "Execution order"
    }

    EbLog {
        int id PK "Auto-increment"
        string definition_id FK "Source definition"
        string action "apply, rollback, import"
        string status "pending, success, partial, failed"
        timestamp started "Action start time"
        timestamp completed "Action end time"
    }
```

### ConfigEntity: EbDefinition

Stores the YAML definition data for import/export and application.

**Key Properties:**
- `id` - Machine name identifier
- `label` - Human-readable label
- `bundle_definitions` - Array of bundle configurations
- `field_definitions` - Array of field configurations with display settings
- `field_group_definitions` - Array of field groups (eb_field_group)
- `display_field_definitions` - Non-default mode overrides

**Storage:** Config sync directory

### ContentEntity: EbRollback / EbRollbackOperation

Rollback storage for undo operations.

**Storage:** Database only (NOT exported with config)

### ContentEntity: EbLog

Tracks apply/rollback/import sessions with timestamps for watchdog queries.

**Storage:** Database only

## Extension Plugin System

```mermaid
flowchart TB
    subgraph Manager["EbExtensionPluginManager"]
        GM[getExtensions]
        GEO[getExtensionsForOperation]
        GEK[getExtensionsForYamlKey]
    end

    subgraph Extensions["Extension Plugins"]
        FG[FieldGroupExtension<br/>yaml_keys: field_group_definitions]
        PA[PathautoExtension<br/>bundle column: pathauto_pattern]
        AEL[AutoEntityLabelExtension<br/>bundle columns: auto_entitylabel_*]
    end

    subgraph Interface["EbExtensionInterface"]
        BO[buildOperations]
        GOD[getOperationDependencies]
        DC[detectChanges]
        EC[extractConfig]
    end

    Manager --> Extensions
    Extensions --> Interface
```

### Available Extensions

| Extension | YAML Key | Columns Added | Operations |
|-----------|----------|---------------|------------|
| `eb_field_group` | `field_group_definitions` | `form_group`, `view_group` on fields | `create_field_group` |
| `eb_pathauto` | - | `pathauto_pattern` on bundles | `create_pathauto_pattern` |
| `eb_auto_entitylabel` | - | `auto_entitylabel_*` on bundles | `configure_auto_entitylabel` |

### Creating Extensions

Extensions implement the `EbExtensionInterface`:

```php
#[EbExtension(
  id: 'my_extension',
  label: new TranslatableMarkup('My Extension'),
  yaml_keys: ['my_definitions'],
  operations: ['create_my_entity'],
)]
class MyExtension extends EbExtensionBase {

  public function buildOperations(array $data): array {
    // Convert my_definitions to operations
  }

}
```

## Rollback Flow

```mermaid
sequenceDiagram
    participant User
    participant RM as RollbackManager
    participant RB as EbRollback
    participant RBO as EbRollbackOperation
    participant OP as OperationProcessor
    participant Drupal as Drupal Entities

    Note over User,Drupal: Apply Definition
    User->>RM: startRollback(definitionId)
    RM->>RB: Create (status: pending)

    loop For each operation
        OP->>Drupal: Execute operation
        OP->>RM: storeRollbackData(result)
        RM->>RBO: Create with undo data
    end

    RM->>RB: Update operation_count

    Note over User,Drupal: Later: Execute Rollback
    User->>RM: executeRollback(rollbackId)
    RM->>RBO: Load all (ORDER BY sequence DESC)

    loop For each operation (reverse order)
        RM->>OP: Execute rollback
        OP->>Drupal: Delete/Restore entity
    end

    RM->>RB: Update status: completed
```

## Key Design Decisions

| Decision | Rationale |
|----------|-----------|
| Field-centric over display-centric | Single source of truth, maps to UI grid, cleaner exports |
| Flat operations over nested structure | Unified processing, explicit dependencies, testability |
| OperationDataBuilder as single source | Consistency across UI, CLI, and API |
| Two-stage validation | Separation of concerns, extensibility |
| Kahn's algorithm | O(V + E) efficiency, mathematical correctness |
| Sync mode as default | Idempotency, efficiency, developer experience |
| ContentEntity for rollback | Database-only storage, not exported with config |
