Claude Code Plugins

Community-maintained marketplace

Feedback

nette-architecture

@nette/claude-code
12
0

Provides Nette application architecture guidelines. Use when working with presenters, modules, directory structure, Core vs Model decisions, or Nette MVC patterns.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name nette-architecture
description Provides Nette application architecture guidelines. Use when working with presenters, modules, directory structure, Core vs Model decisions, or Nette MVC patterns.

For new project skeleton, see skeleton.md.

Backend Architecture

  • The project requires PHP 8.3+, Nette 3.2, Latte 3.1

Key Framework Features

  • Presenters: Controllers that handle HTTP requests in Nette
  • Latte: Templating engine used for views
  • Dependency Injection: Service management through Nette's DI container
  • Router: URL routing through RouterFactory

Directory Structure

The application follows domain-driven organization with preference for flatter structure:

  • App: The main application namespace (App\)
    • Bootstrap: Application initialization and configuration
    • Core: Infrastructure concerns (routing, etc.)
    • Entity: All database entities in single namespace
    • Model: Business logic services organized by domain
    • Presentation: UI layer organized by modules
    • Tasks: Command-line executable tasks

Evolution Strategy

Start minimalGrow organicallyRefactor when painful

Start with flat structure - create subdirectories only when you have 5+ related files or clear implementation variants. Don't architect for theoretical future complexity. Address actual complexity when it emerges with clear user needs driving structural decisions.

Configuration

  • config/common.neon: Main application configuration
  • config/services.neon: Service definitions and auto-wiring configuration

Core vs Model Decision Matrix

Use Core/ for:

  • Technology-agnostic infrastructure (MyExplorer, RouterFactory, QueueMailer)
  • External service integrations (SentryLogger, AI/, GoogleSearch/)
  • Framework extensions and utilities
  • Code that could be moved to another project unchanged

Use Model/ for:

  • Business domain logic (CatalogService, CustomerService, OrderService)
  • Domain-specific operations and rules
  • Entity-specific processing logic
  • Code that knows about your business concepts (products, orders, customers)

Why flat: Entities often cross domain boundaries. ProductRow might be used in catalog, orders, and inventory contexts.

Model Layer Principles

app/Model/
├── CatalogService.php      ← Main domain services at root
├── CustomerService.php
├── OrderService.php
├── mails/                  ← Email templates (specialized assets)
├── Payment/                ← Implementation variants
│   ├── CardOnlinePayment.php
│   ├── BankTransferPayment.php
│   └── CashPayment.php
└── exceptions.php          ← Domain exceptions

Service placement rules:

  1. Main domain coordinator services directly in Model/
  2. Implementation variants get subdirectories when 3+ implementations exist
  3. Specialized assets (templates, exceptions) in focused locations

Module Structure

app/Presentation/
├── Accessory/              ← UI shared across entire application
│   ├── LatteExtension.php
│   └── TemplateFilters.php
├── Admin/
│   ├── BasePresenter.php  ← Admin-specific functionality
│   ├── Auth/              ← Authentication
│   ├── Catalog/           ← Product management
│   │   ├── Brand/
│   │   ├── List/          ← Overview/utility presenters
│   │   └── Product/
│   └── Fulfill/           ← Order processing
└── Front/
	├── Customer/
	└── Listing/

Keep presenters flat until complexity demands structure:

# Start simple
Dashboard/DashboardPresenter.php

# Grow when needed
Admin/Catalog/Product/ProductPresenter.php
Admin/Catalog/Brand/BrandPresenter.php
Admin/Catalog/List/ListPresenter.php

Create nested structure when:

  • Single functional area has 4+ presenters
  • Clear sub-domains emerge (Product management, Order fulfillment)
  • Shared logic between related presenters

Base Presenter Strategy

Create BasePresenter for each major module only when needed:

  • Admin\BasePresenter - authentication checks, admin-specific setup
  • Contains common beforeRender(), authentication logic, shared template variables

Avoid deep inheritance - prefer composition over inheritance chains deeper than BasePresenter → SpecificPresenter.

Accessory Placement Decision Tree

Use Presentation/Accessory/ for:

  • Components used across multiple modules (navigation, forms)
  • Latte extensions and template filters
  • Shared template functionality

Use Module/Accessory/ for:

  • Components specific to that module but used by multiple presenters
  • Module-specific template helpers

Use Presenter directory for:

  • Components used only by that presenter
  • Presenter-specific forms and controls

When to Create New Module

Create module when:

  • You have 5+ related presenters
  • Functionality has distinct user base (Admin vs Front vs Api)
  • Different authentication/authorization requirements
  • Separate URL structure patterns

Avoid modules for:

  • Single presenter with single purpose
  • Artificial separation without clear user/functional boundaries

Tasks and Command Organization

app/Tasks/
├── Maintenance/          ← Cleanup, optimization
├── Integration/          ← External data sync
└── Scheduled/            ← Recurring operations

Task responsibility boundaries:

  • Tasks handle execution context (CLI arguments, error handling, scheduling)
  • Business logic stays in Model services
  • Tasks coordinate, services execute

Anti-Patterns to Avoid

Don't create directories prematurely - wait until you have actual complexity, not anticipated complexity.

Don't separate by technical layer - avoid Services/, Repositories/, Controllers/ separation in favor of domain organization.

Don't create deep hierarchies - prefer descriptive names over nested structure (OrderFulfillmentService vs Fulfill/Order/Service).

Don't duplicate Base presenter logic - use inheritance or traits instead of copying common functionality.

#[Requires] Attribute

Control access to presenter actions and signals:

use Nette\Application\Attributes\Requires;

class AdminPresenter extends BasePresenter
{
	// Require AJAX only
	#[Requires(ajax: true)]
	public function handleDelete(int $id): void
	{
	}

	// Require POST method
	#[Requires(methods: 'POST')]
	public function actionSave(): void
	{
	}

	// Require specific methods
	#[Requires(methods: ['GET', 'POST'])]
	public function actionEdit(int $id): void
	{
	}

	// Require forward (not direct access)
	#[Requires(forward: true)]
	public function actionConfirm(): void
	{
	}

	// Combine requirements
	#[Requires(ajax: true, methods: 'POST')]
	public function handleUpdate(): void
	{
	}
}

Apply to entire presenter:

#[Requires(ajax: true)]
class ApiPresenter extends BasePresenter
{
	// All actions require AJAX
}

Passing Settings to Presenters

Via DI container (recommended):

# config/services.neon
services:
	- App\Presentation\Admin\ProductPresenter(itemsPerPage: 20)
class ProductPresenter extends BasePresenter
{
	public function __construct(
		private int $itemsPerPage,
		private ProductFacade $facade,
	) {}
}

Via parameters:

# config/common.neon
parameters:
	pagination:
		itemsPerPage: 20
		maxItems: 1000
# config/services.neon
services:
	- App\Presentation\ProductPresenter(
		itemsPerPage: %pagination.itemsPerPage%
	)

Via base presenter:

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
	public int $itemsPerPage = 20;

	public function injectSettings(Settings $settings): void
	{
		$this->itemsPerPage = $settings->get('pagination.itemsPerPage', 20);
	}
}