add-domain-entity

Create domain layer components: models, repository interfaces, marshallers, and implementations. Use when: (1) adding domain model in internal/domain/model/, (2) creating repository interface in inter

SKILL.md

GitHub

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

How to Use the Rules

Detailed patterns live in .claude/rules/ (path-scoped, loaded automatically). Before writing each file, Read an existing sibling file of the same category (e.g. internal/domain/model/tenant.go). This loads the matching rule and shows the current real-world pattern. Follow the rule, not memory.

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 CreatedAt and UpdatedAt to the same time parameter
  • Define ReadonlyReference for relations (always nil in constructor)
  • Create slice type alias: type Examples []*Example

Rule: .claude/rules/domain-model.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:generate directive for mock generation
  • Use nullable.Type[T] for optional enum/custom type filter fields
  • Embed BaseGetOptions / BaseListOptions in query structs

Rule: .claude/rules/repository.md (Interface Definition, Query Structs)

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 ReadonlyReference must remain nil (no recursive loading)
  • Use var declaration pattern for nullable timestamp fields
  • Include both ToModel and ToDBModel functions

Rule: .claude/rules/repository.md (Marshaller)

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 buildListQuery helper for reusable filter logic
  • Implement buildPreload helper for relation loading
  • Use base helper functions: addForUpdateFromBaseGetOptions, addForUpdateFromBaseListOptions

Rule: .claude/rules/repository.md (Implementation, Transaction Context)

Step 6: Generate Mocks

make generate.mock

This generates mock implementations in internal/domain/repository/mock/.

Checklist

Domain Model

  • Entity struct with all fields
  • ReadonlyReference for relations (if any)
  • Constructor with id.New() and ReadonlyReference: 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 Unknown as first constant
  • String() and Valid() methods
  • New{Type}(str string) constructor

Repository

  • Interface with //go:generate directive
  • 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 ReadonlyReference correctly
  • Repository implementation with all CRUD methods
  • buildListQuery and buildPreload helpers
  • 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