---
name: dbio-perl-core
description: DBIO-Klassenpattern mit CAG (Class::Accessor::Grouped). Nutze wenn du DBIO-Klassen baust oder refactorst.
user-invocable: false
allowed-tools: Read, Grep, Glob
model: sonnet
---

# Perl DBIO Class Patterns mit CAG

## Das Grundprinzip

DBIO nutzt **kein Moo, kein Moops, keine Role::Tiny**. Alles folgt dem CAG-Pattern (Class::Accessor::Grouped) und dem Prinzip: **kleines Interface, tiefe Implementation**.

```perl
# Statt Moo:
#   has 'host' => (is => 'rw', isa => 'Str');

# DBIO CAG:
__PACKAGE__->mk_group_accessors(simple => qw/host port user password/);
```

Der generierte Accessor `host()` speichert nicht direkt. Er ruft intern `get_simple('host')` oder `set_simple('host', $value)` auf. Das Interface bleibt klein — die Logik steckt in `get_*`/`set_*`.

---

## Accessor-Gruppen

### `simple` — Instanzdaten

```perl
__PACKAGE__->mk_group_accessors(simple => qw(_storage _credentials _read_index));
```

Speichert direkt im Objekt-Hash. `$obj->host(1)` → `set_simple('host', 1)`.

### `inherited` — vererbbare Klassendaten

```perl
__PACKAGE__->mk_group_accessors(inherited => qw(sql_name_sep sql_quote_char));
__PACKAGE__->sql_name_sep('.');
```

Wenn Subklasse nichts setzt, wird in Elternklassen via `mro::get_linear_isa` gesucht. DBIO nutzt das für SQL-Defaults in `Storage::DBI`.

### `component_class` — lazy ladende Klassen

```perl
__PACKAGE__->mk_group_accessors(component_class => qw(cursor_class resultset_class));
__PACKAGE__->cursor_class('DBIO::Cursor');
```

DBIO überschreibt `get_component_class` in `DBIO::Base`:
1. Holt Wert via `get_inherited`
2. Lädt Klasse via `ensure_class_loaded`
3. Gibt geladene Klasse zurück

Danach wird direkt auf der Klasse weitergearbeitet:

```perl
$self->cursor_class->new($self, \@_, $attrs);
$self->resultset_class->new($self, ...);
```

**Wichtig:** `component_class` ist keine automatische Delegation. Es gibt eine Klasse zurück — danach ruft man Methoden auf dieser Klasse auf.

### Eigene Gruppen (für Domänenlogik)

```perl
__PACKAGE__->mk_group_accessors(column => 'title');
```

Die Gruppe `column` bekommt eigene `get_column`/`set_column` Handler. DBIO's `Row` nutzt das für dynamische Spaltenaccessors:

```perl
sub Row::get_column { ... raw value, inflation ... }
sub Row::set_column { ... dirty tracking ... }
```

---

## Reiner Perl-Konstruktor

```perl
sub new {
  my ($class, @args) = @_;
  my $self = bless {}, $class;
  $self->host($args{host}) if exists $args{host};
  return $self;
}
```

**Immer**: `bless {}`, nicht `bless []`. Direkte Bless-Form, kein `Moo::Object`.

---

## base vs. Role::Tiny

**Falsch** (Role::Tiny):
```perl
use Role::Tiny;
with 'SomeRole';
```

**Richtig** (CAG Base Class):
```perl
use base qw/DBIO::Base Class::Accessor::Grouped/;
```

Für CredentialsProvider etc. — eine **Basisklasse**, keine Rolle. Rolle würde `requires()` brauchen, was CAG nicht kann.

---

## load_components — NUR für Results

`load_components` ist ein DBIO-spezifisches System das:
1. Modul lazy lädt
2. `mk_group_accessors` aufruft
3. Ergebnis in `$class` speichert

**Nur** für `DBIO::Result` Subklassen. Nicht für Storage, nicht für AccessBroker, nicht für allgemeine Komposition.

Richtige Nutzung:
```perl
package MyApp::Schema::Result::Artist;
use base qw/DBIO::Core/;
__PACKAGE__->load_components('InflateColumn::DateTime');
```

Falsche Nutzung (NICHT für AccessBroker!):
```perl
package DBIO::AccessBroker::Credentials;
# NICHT: __PACKAGE__->load_components('Role::Tiny') ...
```

---

## Deep Module Pattern

CAG trennt sauber:

```perl
# 1. Kleines öffentliches Interface:
$row->title;
$row->title('New');

# 2. Accessor-Gruppe deklarieren:
__PACKAGE__->mk_group_accessors(column => 'title');

# 3. Tiefe Semantik in get_column/set_column:
sub get_column {
  my ($self, $col) = @_;
  # inflation cache, validation, lazy load ...
}
```

Das erlaubt:
- `InflateColumn` ersetzt `column`-Handler durch `inflated_column` für bestimmte Spalten
- `FilterColumn` ersetzt für skalare Filter
- `Row` erzeugt dynamische Accessors pro Datenbankspalte

**Faustregel:**

```perl
__PACKAGE__->mk_group_accessors(simple => qw/runtime_state/);
__PACKAGE__->mk_group_accessors(inherited => qw/config_knob/);
__PACKAGE__->mk_group_accessors(component_class => qw/strategy_class/);
```

`simple` für Objektzustand, `inherited` für vererbbare Konfiguration, `component_class` für austauschbare Implementationsklassen.

---

## Keine Magie —pure Perl

Kein `has` (Moo-Syntax), kein `with` (Role::Tiny), kein `requires`. Alles explizit:

```perl
package DBIO::AccessBroker::Credentials;
use base qw/Class::Accessor::Grouped/;
__PACKAGE__->mk_group_accessors('simple' => qw(_storage _credentials _credentials_provider _base_params _read_index));

sub new {
  my ($class, @args) = @_;
  my $self = bless {}, $class;
  # ... direkt arbeiten
}
```

Bei Problemen: `_build_*` Methoden (private Builder) statt `BUILD` (Moo).