Lifecycle Hooks in Practice
Tagex lets a struct define optional lifecycle hooks that run
around directive execution.
This guide shows how to use Before, Success,
and Failure together to model real, production workflows.
The Problem: Success Is Implicit
In most Go code, success is inferred by the absence of errors. That makes it hard to centralize side effects, commit work, or emit events only when all tagged behavior has succeeded.
Lifecycle hooks make the success path explicit and structured.
The Hooks
- Before() error runs before any directives execute
- Success() runs only if all directives succeed
- Failure(err) runs if any directive fails
Tagex does not interpret these hooks. They are owned by the struct and can do anything: initialize resources, write logs, persist data, or roll back.
Example: Importing a Row with a Commit on Success
type ImportRow struct {
SKU string `norm:"upper"`
Price string `norm:"currency, locale=nl_NL"`
Notes string `audit:"log, event=import"`
}
type ImportJob struct {
Row ImportRow
LogID string
}
func (j *ImportJob) Before() error {
logID, err := startAuditLog()
if err != nil {
return err
}
j.LogID = logID
return nil
}
func (j *ImportJob) Success() {
persistRow(j.Row)
finalizeAuditLog(j.LogID, "success")
}
func (j *ImportJob) Failure(err error) {
finalizeAuditLog(j.LogID, "failure")
recordFailure(err)
}
The directives handle normalization and audit tagging. The hooks handle orchestration and side effects.
Execution Flow
Before()runs- Tagged directives execute in order
- If all succeed,
Success()runs - If any fail,
Failure(err)runs
This makes the success path explicit and reliable.
If Success() runs, you know all directives completed.
Before() gates execution, directives run inside
ProcessStruct, resulting in either Success() or Failure(err).
When to Use Each Hook
- Before(): allocate resources, start timers, load context
- Success(): commit work, emit events, persist outcomes
- Failure(err): log errors, roll back, update metrics
Hooks are a boundary between semantic behavior (directives) and orchestration (side effects).
Common Pitfalls
- Doing heavy work in directives that belongs in
Success() - Duplicating error handling in directives and
Failure(err) - Using
Before()for semantic validation instead of setup and gating
Keep directives focused on field-level semantics.
Use hooks to coordinate the wider operation.
If Before() returns an error, execution halts and the struct
is not processed, making it a clean place to enforce preconditions.