---
layout: 'page'
uri: '/framework/application/events'
position: 4
slug: 'framework-application-events'
parent: 'framework-application'
navTitle: 'Events'
title: 'Events'
description: 'Domain events -- jak vyhlásit "stalo se X" tak, aby na to reagoval kdokoli další, aniž by o tom command handler musel vědět.'
---

# Events


## K čemu ti to je

Command handler má dělat jednu věc. Když po vytvoření uživatele přijde požadavek "a taky pošli welcome email", nechceš, aby `CreateUserHandler` znal mailer -- jinak ho bude za měsíc znát i Slack notifier, audit a indexer.

Lepší: handler **vyhlásí event** `UserCreated` a neřeší, kdo na to reaguje. Mailer (nebo cokoli dalšího) se na ten event zaregistruje samostatně.

Tři garance:

1. **Loose coupling.** Command ví, co se stalo, ne co se má teď stát.
2. **Atomicita s commitem.** Event se rozešle handlerům až **po úspěšném commitu**. Když commit selže, event se zahodí -- žádný welcome mail pro uživatele, který v DB nevznikl.
3. **Izolace mezi requesty.** Paralelní requesty mají vlastní sběrače, nepřelévá se to.


## Krok za krokem

Scénář: po `CreateUser` poslat welcome email.

### 1. Event struct v doméně (jen primitivy)

`domain/user/user_created.go`:

```go
type UserCreated struct {
    UserID, Nickname, Email string
    Timestamp               time.Time
}

func (e UserCreated) EventName() string     { return "user.created" }
func (e UserCreated) OccurredAt() time.Time { return e.Timestamp }
```

### 2. Vyhlas event v command handleru -- **až po úspěšném zápisu**

```go
if err := h.users.Save(ctx, u); err != nil {
    return err
}
shared.EventCollectorFromContext(ctx).Collect(user.UserCreated{...})
return nil
```

### 3. Napiš handler

`application/user/event/send_welcome_email.go`:

```go
func (h *SendWelcomeEmailHandler) Handle(ctx context.Context, event shared.DomainEvent) error {
    e := event.(user.UserCreated)
    return h.mailer.Send(e.Email, "Welcome!", /* ... */)
}
```

### 4. Zaregistruj v `provideEventHandlers`

`infrastructure/di/container_provider.go` -- jediné místo, stejný pattern jako [permissions](/guides/permissions), scheduler joby, job handlery:

```go
func provideEventHandlers(welcome *eventcmd.SendWelcomeEmailHandler) []bus.EventHandlerEntry {
    return []bus.EventHandlerEntry{
        {Event: "user.created", Handler: welcome.Handle},
    }
}
```

`make di` a hotovo.


## Co se ti hodí vědět

- **Dispatch je synchronní v request goroutině.** Pomalý handler prodlouží HTTP response. Pro odeslání mailu, externí API a podobné věci použij [Job Queue](/framework/infrastructure/job-queue) -- handler tam jen `JobDispatcherFromContext(ctx).Enqueue(...)` a vrátí se hned.
- **Když handler selže**, command už commitnul -- error se zaloguje, uživatel dostane 200/201. Z handleru nemůžeš odvolat command.
- **Eventy = primitivy** (`string`, `time.Time`), žádné entity ani VOs. Aby je mohl konzumovat cizí kontext bez importu a aby šly serializovat pro job queue.
- **Bez kaskády (strojově vynuceno)**: když event handler zavolá `Collect(...)`, runtime panic s jasnou hláškou. Pro další asynchronní práci použij `JobDispatcher`.
- **Mimo bus** (CLI `create-user`) vrátí `EventCollectorFromContext` *throwaway* sběrač -- `Collect` projde, ale eventy nikam nejdou. Žádný welcome mail pro seedovaného admina.


## Co lze nastavit

Vše v `infrastructure/di/container_provider.go`, žádné env proměnné.

| Co | Kde | Default |
|---|---|---|
| Které eventy aplikace zná | `provideEventHandlers()` | prázdný slice |
| Více handlerů na stejný event | Více entries se stejným `Event` | volají se v pořadí registrace, sériově |
| Middleware kolem dispatchu | `provideEventBus()` -- `bus.NewEventBus(...)` | `Recovery + Logging` |
| Sync → async dispatch | n/a | Není konfigurovatelné. Pro async přesuň handler do [Job Queue](/framework/infrastructure/job-queue) |

---

[← Queries](/framework/application/queries.md) | [Audit log →](/framework/application/audit.md)