Invoke BEFORE designing presenters, modules, or application structure in web applicartion.
/plugin marketplace add nette/claude-code/plugin install nette@netteThis skill inherits all available tools. When active, it can use any tool Claude has access to.
skeleton.mdFor new project skeleton, see skeleton.md.
RouterFactoryThe application follows domain-driven organization with preference for flatter structure:
App\)
Start minimal → Grow organically → Refactor 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.
Use Core/ for:
Use Model/ for:
Why flat: Entities often cross domain boundaries. ProductRow might be used in catalog, orders, and inventory contexts.
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:
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:
Create BasePresenter for each major module only when needed:
Admin\BasePresenter - authentication checks, admin-specific setupbeforeRender(), authentication logic, shared template variablesAvoid deep inheritance - prefer composition over inheritance chains deeper than BasePresenter → SpecificPresenter.
Use Presentation/Accessory/ for:
Use Module/Accessory/ for:
Use Presenter directory for:
Create module when:
Avoid modules for:
app/Tasks/
├── Maintenance/ ← Cleanup, optimization
├── Integration/ ← External data sync
└── Scheduled/ ← Recurring operations
Task responsibility boundaries:
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.
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
}
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);
}
}
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.