157 lines
3.5 KiB
Markdown
157 lines
3.5 KiB
Markdown
# Domain Entity Scaffold Template
|
|
|
|
Generate a domain entity with factory constructor, value objects, and errors.
|
|
|
|
## Placeholders
|
|
|
|
- `{{Name}}` — PascalCase entity name (e.g., `Training`, `Hour`, `Order`)
|
|
- `{{name}}` — camelCase (e.g., `training`)
|
|
- `{{name_lower}}` — all lowercase package name (e.g., `training`)
|
|
- `{{name_snake}}` — snake_case (e.g., `training`)
|
|
|
|
## File: `domain/{{name_lower}}/{{name_snake}}.go`
|
|
|
|
```go
|
|
package {{name_lower}}
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// {{Name}} is the aggregate root for the {{name_lower}} domain.
|
|
type {{Name}} struct {
|
|
uuid string
|
|
createdAt time.Time
|
|
// TODO: Add domain fields (all private)
|
|
// status Status // value object, not raw string
|
|
}
|
|
|
|
// New{{Name}} creates a new {{Name}} with validated invariants.
|
|
func New{{Name}}(uuid string) (*{{Name}}, error) {
|
|
if uuid == "" {
|
|
return nil, errors.New("empty {{name_lower}} uuid")
|
|
}
|
|
|
|
return &{{Name}}{
|
|
uuid: uuid,
|
|
createdAt: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
// Unmarshal{{Name}}FromDatabase reconstructs a {{Name}} from persistence.
|
|
// Bypasses validation — data was valid when stored.
|
|
func Unmarshal{{Name}}FromDatabase(
|
|
uuid string,
|
|
createdAt time.Time,
|
|
// TODO: Add all persisted fields
|
|
) *{{Name}} {
|
|
return &{{Name}}{
|
|
uuid: uuid,
|
|
createdAt: createdAt,
|
|
}
|
|
}
|
|
|
|
// Accessor methods — expose state without allowing mutation.
|
|
|
|
func (t {{Name}}) UUID() string {
|
|
return t.uuid
|
|
}
|
|
|
|
func (t {{Name}}) CreatedAt() time.Time {
|
|
return t.createdAt
|
|
}
|
|
|
|
// TODO: Add behavior methods using domain language.
|
|
// Examples:
|
|
//
|
|
// func (t *{{Name}}) Approve() error {
|
|
// if t.status != Pending {
|
|
// return ErrNotPending
|
|
// }
|
|
// t.status = Approved
|
|
// return nil
|
|
// }
|
|
//
|
|
// func (t *{{Name}}) Cancel() error { ... }
|
|
// func (t *{{Name}}) Submit(details string) error { ... }
|
|
```
|
|
|
|
## File: `domain/{{name_lower}}/errors.go`
|
|
|
|
```go
|
|
package {{name_lower}}
|
|
|
|
import "errors"
|
|
|
|
// Sentinel errors — simple, no context needed.
|
|
var (
|
|
ErrNotFound = errors.New("{{name_lower}} not found")
|
|
// TODO: Add domain-specific errors
|
|
// ErrAlreadyCanceled = errors.New("{{name_lower}} already canceled")
|
|
// ErrNotPending = errors.New("{{name_lower}} is not in pending state")
|
|
)
|
|
|
|
// Typed errors — carry context for logging/display.
|
|
// Example:
|
|
//
|
|
// type ForbiddenError struct {
|
|
// RequestingUserUUID string
|
|
// OwnerUUID string
|
|
// }
|
|
//
|
|
// func (e ForbiddenError) Error() string {
|
|
// return fmt.Sprintf("user %s cannot access {{name_lower}} owned by %s",
|
|
// e.RequestingUserUUID, e.OwnerUUID)
|
|
// }
|
|
```
|
|
|
|
## File: `domain/{{name_lower}}/status.go` (Optional Value Object)
|
|
|
|
```go
|
|
package {{name_lower}}
|
|
|
|
import "fmt"
|
|
|
|
// Status is a value object — cannot be constructed with arbitrary values.
|
|
type Status struct {
|
|
s string
|
|
}
|
|
|
|
var (
|
|
Pending = Status{"pending"}
|
|
Approved = Status{"approved"}
|
|
Canceled = Status{"canceled"}
|
|
)
|
|
|
|
func NewStatusFromString(s string) (Status, error) {
|
|
switch s {
|
|
case "pending":
|
|
return Pending, nil
|
|
case "approved":
|
|
return Approved, nil
|
|
case "canceled":
|
|
return Canceled, nil
|
|
default:
|
|
return Status{}, fmt.Errorf("unknown {{name_lower}} status: %s", s)
|
|
}
|
|
}
|
|
|
|
func (s Status) String() string {
|
|
return s.s
|
|
}
|
|
|
|
func (s Status) IsZero() bool {
|
|
return s == Status{}
|
|
}
|
|
```
|
|
|
|
## Post-Creation Checklist
|
|
|
|
- [ ] All struct fields are private (unexported)
|
|
- [ ] Factory constructor validates all invariants
|
|
- [ ] UnmarshalFromDatabase accepts all persisted fields
|
|
- [ ] Value objects are struct wrappers, not type aliases
|
|
- [ ] Behavior methods use domain language, not CRUD
|
|
- [ ] Errors are sentinel vars or typed structs
|