Add Domain Entity
Create domain layer components for a new entity following DDD patterns.
Prerequisites
- •Database table created (use add-database-table skill first)
- •SQLBoiler model generated via
make migrate.up
Quick Workflow
1. Domain Model → internal/domain/model/{entity}.go
2. Domain Error → internal/domain/errors/errors.go
3. Repository Interface → internal/domain/repository/{entity}.go
4. Marshaller → internal/infrastructure/{db}/internal/marshaller/{entity}.go
5. Repository Impl → internal/infrastructure/{db}/repository/{entity}.go
6. Generate Mocks → make generate.mock
Step 1: Create Domain Model
Location: internal/domain/model/{entity}.go
Create the entity struct, constructor, update methods, and type aliases.
Key requirements:
- •Use
id.New()for ID generation in constructor - •Set both
CreatedAtandUpdatedAtto the same time parameter - •Define
ReadonlyReferencefor relations (always nil in constructor) - •Create slice type alias:
type Examples []*Example
See: references/domain-model-patterns.md
Step 2: Add Domain Error
Location: internal/domain/errors/errors.go
Add a not-found error for the entity:
ExampleNotFoundErr = NewNotFoundError("E2xxxxx", "Example not found")
Follow error code conventions from .claude/rules/domain-errors.md.
Step 3: Create Repository Interface
Location: internal/domain/repository/{entity}.go
Define the repository interface with standard CRUD operations and query structs.
Key requirements:
- •Add
//go:generatedirective for mock generation - •Use
nullable.Type[T]for optional enum/custom type filter fields - •Embed
BaseGetOptions/BaseListOptionsin query structs
See: references/repository-patterns.md
Step 4: Create Marshaller
Location: internal/infrastructure/{mysql|postgresql|spanner}/internal/marshaller/{entity}.go
Convert between DB models and domain models.
Key requirements:
- •Related entity's
ReadonlyReferencemust remain nil (no recursive loading) - •Use var declaration pattern for nullable timestamp fields
- •Include both
ToModelandToDBModelfunctions
See: references/marshaller-patterns.md
Step 5: Create Repository Implementation
Location: internal/infrastructure/{mysql|postgresql|spanner}/repository/{entity}.go
Implement the repository interface using SQLBoiler.
Key requirements:
- •Use
transactable.GetContextExecutor(ctx)for all DB operations - •Implement
buildListQueryhelper for reusable filter logic - •Implement
buildPreloadhelper for relation loading - •Use base helper functions:
addForUpdateFromBaseGetOptions,addForUpdateFromBaseListOptions
See: references/repository-patterns.md
Step 6: Generate Mocks
make generate.mock
This generates mock implementations in internal/domain/repository/mock/.
Checklist
Domain Model
- • Entity struct with all fields
- •
ReadonlyReferencefor relations (if any) - • Constructor with
id.New()andReadonlyReference: nil - • Update method using
null.*types for optional fields - • Slice type alias (
Examples []*Example) - • Helper methods on slice (
IDs(),MapByID())
Status/Enum Types (if needed)
- • Type definition with
Unknownas first constant - •
String()andValid()methods - •
New{Type}(str string)constructor
Repository
- • Interface with
//go:generatedirective - • Query structs with
nullable.Type[T]for optional enums - • Domain error added for not-found case
Implementation
- • Marshaller with
ToModel,ToDBModel,ToModels,ToDBModels - • Marshaller handles
ReadonlyReferencecorrectly - • Repository implementation with all CRUD methods
- •
buildListQueryandbuildPreloadhelpers - • Mocks generated
Next Steps
After creating domain entity, use add-api-endpoint skill to create:
- •Usecase input/output structs
- •Interactor interface and implementation
- •Protocol Buffers definition
- •gRPC handler