---
layout: 'page'
uri: '/framework/application/commands'
position: 2
slug: 'framework-application-commands'
parent: 'framework-application'
navTitle: 'Commands'
title: 'Commands'
description: 'Balíček application/command/ -- write operace, validace, Permissioned interface.'
---
# Commands
## Proč
Commands reprezentují write operace -- mění stav systému. Každý command je čistý data struct + handler s business logikou. Handler závisí výhradně na doménových interfaces (např. `user.Repository`, `shared.PasswordHasher`), nikdy na infrastruktuře.
## Jak
Každý command = dva typy v jednom souboru:
- `XxxCommand` -- data struct (raw hodnoty z HTTP requestu)
- `XxxHandler` -- logika (validace, business pravidla, zápis)
### Příklad
```go
// application/user/command/create_user.go
type CreateUserCommand struct {
Nickname string
Password string
Email string
Role string
}
func (c CreateUserCommand) RequiredPermission() string { return "admin:users:create" }
type CreateUserHandler struct {
users user.Repository // doménový interface
password shared.PasswordHasher // doménový interface
}
func (h *CreateUserHandler) Handle(ctx context.Context, cmd CreateUserCommand) error {
// 1. Vstupní validace přes domain value objects
nickname, err := user.NewNickname(cmd.Nickname)
if err != nil {
return err
}
role, err := user.NewRole(cmd.Role)
if err != nil {
return err
}
password, err := user.NewPassword(cmd.Password)
if err != nil {
return err
}
email, err := user.NewEmail(cmd.Email)
if err != nil {
return err
}
// 2. Business pravidlo (I/O) -- unique nickname
existing, err := h.users.FindByNickname(ctx, string(nickname))
if err != nil {
return err
}
if existing != nil {
return &shared.ValidationError{
Field: "nickname",
Message: "user with this nickname already exists",
}
}
// 3. Hash hesla + vytvoření entity + zápis
hash, err := h.password.Hash(string(password))
if err != nil {
return err
}
u := user.NewUser(nickname, hash, email, role)
if err := h.users.Save(ctx, u); err != nil {
return err
}
// 4. Domain event -- per-request collector v ctx, bus ho dispatchne po commitu
shared.EventCollectorFromContext(ctx).Collect(user.UserCreated{
UserID: u.ID,
Nickname: u.Nickname,
Email: u.Email,
Role: u.Role,
Timestamp: time.Now(),
})
return nil
}
```
### Dispatch z HTTP handleru
```go
err := bus.ExecVoid(ctx, h.commandBus.Bus, "CreateUser", cmd, func(ctx context.Context) error {
return h.createUser.Handle(ctx, cmd)
})
```
## Detaily
- Command struct nemá logiku -- jen data.
- Handler nikdy neimportuje `infrastructure/security/`, `infrastructure/sqlite/` ani `application/bus/`. Wire injektuje doménové interfaces.
- Permission se deklaruje přes `Permissioned` interface -- kontrolu provádí `AuthorizeMiddleware` v busu.
- Validace: domain value objects (formát) + repo queries (unikátnost, existence).
- Transakce a event dispatch řídí bus middleware -- handler o nich neví.
---
[← Bus](/framework/application/bus.md) | [Queries →](/framework/application/queries.md)