---
name: database-domain
description: Use when designing or refactoring internal/store/{domain} packages, domain Store structs, domain service boundaries, or cross-domain transaction coordination. Enforces the rule that domain packages own their Store/Service, API handlers depend on narrow port interfaces, and store domain types never leak into API DTOs.
---

# Database Domain

## Core rule

Each domain under `internal/store/{domain}` owns its data access and business logic. API handlers never import `internal/store/{domain}` or `internal/store/model` directly — they depend on narrow port interfaces defined in `ports.go` and receive DTOs, never store types.

## Domain package structure

Each `internal/store/{domain}` package contains:

- `service.go` (or `store.go`): The domain Store/Service struct with its own `coredb.DB` connection.
- `model.go`: Domain-local model types (if different from shared `internal/store/model`).
- Domain method files: One file per feature area (e.g., `messages.go`, `threads.go`).

```
internal/store/message/
├── service.go          # MessageStore{db coredb.DB}
├── model.go            # Domain-local query row types
├── messages.go         # AppendMessage, GetHistory, etc.
├── threads.go          # Thread management methods
└── dependencies.go     # Cross-domain dependency interfaces
```

## Domain Store struct

Each domain Store holds its own database connection:

```go
type MessageStore struct {
    db coredb.DB
}
```

- Domain Stores do not hold a reference to the root `*store.Store`.
- Cross-domain calls go through explicit dependency interfaces, not through the root Store.
- Domain Stores may define local dependency interfaces for other domains they need:

```go
// In internal/store/message/dependencies.go
type OrganizationGetter interface {
    GetMemberRole(ctx context.Context, orgID, userID string) (string, error)
}
```

## Transaction boundaries

- Single-domain operations use the domain's own `db` field.
- Cross-domain operations receive an explicit `coredb.Tx` parameter:

```go
func (s *MessageStore) AppendMessage(ctx context.Context, tx coredb.Tx, msg Message) error {
    // Use tx for cross-domain transactional writes
}
```

- Domain methods that participate in cross-domain transactions provide two signatures:
  - `GetXXX(ctx, ...)`: reads using `s.db`
  - `AppendXXX(ctx, tx coredb.Tx, ...)`: writes within a transaction

## Port interface pattern

API handlers define narrow port interfaces in their `ports.go`:

```go
// In internal/api/{domain}/ports.go
type MessageStore interface {
    GetHistory(ctx context.Context, channelID string, limit int) ([]storemessage.ReceiveMessage, error)
    AppendMessage(ctx context.Context, tx coredb.Tx, msg storemessage.CreateMessageParams) (storemessage.ReceiveMessage, error)
}
```

- `ports.go` and `routes.go`/`wiring.go` are the **only** API files allowed to import `internal/store/{domain}` or `internal/store/model`.
- `api_*.go` handler files and `internal/api/dto` must never import store packages.

## DTO conversion boundary

When a port method returns a store domain type, conversion to DTO happens at the wiring boundary:

```go
// In internal/api/{domain}/presenters.go or mappers.go
func messageToDTO(m storemessage.ReceiveMessage) dto.ReceiveMessage {
    return dto.ReceiveMessage{
        ID:      m.ID,
        Content: m.Content,
        // ... map all fields explicitly
    }
}
```

- Presenters/mappers may import store domain packages (they are part of the wiring boundary).
- Handlers only see and return DTO types.

## Anti-patterns to avoid

- Do not let `api_*.go` files import `internal/store/{domain}` or `internal/store/model`.
- Do not let DTO files import any `internal/store*` package.
- Do not embed store model/domain types in DTO structs.
- Do not pass `*store.Store` (root) to API handlers — use narrow domain ports.
- Do not hold cross-domain direct struct references in a domain Store; use dependency interfaces.
