Awesome-claude-code create-read-model

Generates Read Model/Projection for PHP 8.4. Creates optimized query models for CQRS read side with projections and denormalization. Includes unit tests.

install
source · Clone the upstream repo
git clone https://github.com/dykyi-roman/awesome-claude-code
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/dykyi-roman/awesome-claude-code "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/create-read-model" ~/.claude/skills/dykyi-roman-awesome-claude-code-create-read-model && rm -rf "$T"
manifest: skills/create-read-model/SKILL.md
source content

Read Model / Projection Generator

Creates Read Model infrastructure for CQRS read side with optimized query models.

When to Use

ScenarioExample
CQRS read sideSeparate query models
Denormalized viewsDashboard aggregates
Complex queriesMulti-entity joins
Event-driven updatesEvent projections

Component Characteristics

Read Model

  • Optimized for queries
  • Denormalized data
  • Eventually consistent
  • No business logic

Projection

  • Builds read models from events
  • Handles event streams
  • Maintains synchronization
  • Idempotent processing

Repository

  • Query-focused methods
  • Returns read models
  • No write operations

Generation Process

Step 1: Generate Domain Read Model

Path:

src/Domain/{BoundedContext}/ReadModel/

  1. {Name}ReadModel.php
    — Immutable read model with fromArray/toArray
  2. {Name}ReadModelRepositoryInterface.php
    — Query-focused repository interface

Step 2: Generate Application Projection

Path:

src/Application/{BoundedContext}/Projection/

  1. {Name}ProjectionInterface.php
    — Projection contract
  2. {Name}Projection.php
    — Event handlers building read model

Step 3: Generate Infrastructure

Path:

src/Infrastructure/{BoundedContext}/

  1. Projection/{Name}Store.php
    — Store for insert/update/upsert
  2. ReadModel/Doctrine{Name}Repository.php
    — Repository implementation

Step 4: Generate Tests

  1. {Name}ReadModelTest.php
    — Read model serialization tests
  2. {Name}ProjectionTest.php
    — Projection event handling tests

File Placement

ComponentPath
Read Model
src/Domain/{BoundedContext}/ReadModel/
Repository Interface
src/Domain/{BoundedContext}/ReadModel/
Projection Interface
src/Application/{BoundedContext}/Projection/
Projection
src/Application/{BoundedContext}/Projection/
Store
src/Infrastructure/{BoundedContext}/Projection/
Repository Impl
src/Infrastructure/{BoundedContext}/ReadModel/
Unit Tests
tests/Unit/

Naming Conventions

ComponentPatternExample
Read Model
{Name}ReadModel
OrderSummaryReadModel
Repository Interface
{Name}ReadModelRepositoryInterface
OrderSummaryReadModelRepositoryInterface
Projection Interface
{Name}ProjectionInterface
OrderSummaryProjectionInterface
Projection
{Name}Projection
OrderSummaryProjection
Store
{Name}Store
OrderSummaryStore
Test
{ClassName}Test
OrderSummaryProjectionTest

Quick Template Reference

Read Model

final readonly class {Name}ReadModel
{
    public function __construct(
        public string $id,
        // ... denormalized properties
        public \DateTimeImmutable $createdAt,
        public \DateTimeImmutable $updatedAt
    ) {}

    public static function fromArray(array $data): self;
    public function toArray(): array;
}

Projection

final class {Name}Projection implements {Name}ProjectionInterface
{
    public function project(DomainEventInterface $event): void
    {
        match ($event::class) {
            OrderCreated::class => $this->whenOrderCreated($event),
            OrderShipped::class => $this->whenOrderShipped($event),
            default => null,
        };
    }

    public function reset(): void;
    public function subscribedEvents(): array;
}

Usage Example

// Query read model
$orders = $orderSummaryRepository->findByCustomerId($customerId);

// Project event
$projection->project($orderCreatedEvent);

// Reset projection for rebuild
$projection->reset();

Database Schema

CREATE TABLE order_summaries (
    id VARCHAR(36) PRIMARY KEY,
    order_number VARCHAR(50) NOT NULL UNIQUE,
    customer_id VARCHAR(36) NOT NULL,
    customer_name VARCHAR(255) NOT NULL,
    status VARCHAR(50) NOT NULL,
    total_cents BIGINT NOT NULL,
    created_at TIMESTAMP NOT NULL,
    updated_at TIMESTAMP NOT NULL,

    INDEX idx_customer (customer_id),
    INDEX idx_status (status)
);

Anti-patterns to Avoid

Anti-patternProblemSolution
Business LogicRead model has behaviorKeep data-only
Write OperationsModifying read modelsUse projections only
Non-idempotentRe-projection breaks dataIdempotent event handling
Missing ResetCan't rebuildAdd reset() method
Tight CouplingProjection depends on domainUse events only

References

For complete PHP templates and examples, see:

  • references/templates.md
    — Read model, projection, store templates
  • references/examples.md
    — OrderSummary example and tests
  • references/event-sourcing-projections.md
    — Event replay projections, versioning, checkpoint tracking, async workers