Loading...
Loading...
This skill should be used when implementing DDD tactical design patterns in Go, including Entities, Value Objects, Aggregates, Repositories, Domain Services, Domain Events, Factories, and Specifications.
npx skill4agent add baotoq/agent-skills golang-ddd*TTNewX()internal/domain/context.Contextinternal/
├── domain/ # Domain layer (no external deps)
│ ├── customer/ # One package per aggregate
│ │ ├── customer.go # Aggregate root entity
│ │ ├── repository.go # Repository interface
│ │ ├── email.go # Value objects
│ │ ├── events.go # Domain events
│ │ └── errors.go # Domain errors
│ ├── order/
│ │ ├── order.go
│ │ ├── repository.go
│ │ ├── item.go # Child entity
│ │ └── money.go # Value object
│ └── shared/ # Shared kernel
│ ├── events.go # Event interface
│ └── specification.go # Generic specification
│
├── application/ # Application services (orchestration)
│ ├── command/
│ │ └── place_order.go
│ └── query/
│ └── get_customer.go
│
└── infrastructure/ # Technical implementations
├── postgres/
│ ├── customer_repo.go
│ └── order_repo.go
└── eventbus/
└── in_memory.go| Pattern | Go Idiom | Receiver | Identity |
|---|---|---|---|
| Entity | Struct + pointer receiver | | By ID |
| Value Object | Type alias or struct + value receiver | | By value |
| Aggregate Root | Entity + unexported children | | By ID |
| Repository | Interface in domain package | N/A | N/A |
| Domain Service | Stateless struct with deps | | N/A |
| Domain Event | Immutable struct | | By name+time |
| Factory | | N/A | N/A |
| Specification | Generic interface | | N/A |
EmailMoneyAddressinfrastructure/references/entities-and-value-objects.mdreferences/aggregates-and-repositories.mdreferences/events-and-specifications.mdreferences/anti-patterns.mdtype Order struct {
id uuid.UUID
status Status
items []Item
createdAt time.Time
}
func NewOrder(customerID uuid.UUID) (*Order, error) {
return &Order{
id: uuid.New(),
status: StatusDraft,
items: make([]Item, 0),
createdAt: time.Now(),
}, nil
}
func (o *Order) AddItem(product ProductID, qty int, price Money) error {
if o.status != StatusDraft {
return ErrOrderNotDraft
}
o.items = append(o.items, NewItem(product, qty, price))
return nil
}type Money struct {
amount int64
currency string
}
func NewMoney(amount int64, currency string) (Money, error) {
if currency == "" {
return Money{}, ErrInvalidCurrency
}
return Money{amount: amount, currency: currency}, nil
}
func (m Money) Add(other Money) (Money, error) {
if m.currency != other.currency {
return Money{}, ErrCurrencyMismatch
}
return Money{amount: m.amount + other.amount, currency: m.currency}, nil
}func (o *Order) Place() error {
if len(o.items) == 0 {
return ErrEmptyOrder
}
if o.status != StatusDraft {
return ErrOrderNotDraft
}
o.status = StatusPlaced
o.events = append(o.events, NewOrderPlacedEvent(o.id, o.Total()))
return nil
}// domain/order/repository.go
type Repository interface {
Find(ctx context.Context, id uuid.UUID) (*Order, error)
Save(ctx context.Context, order *Order) error
Update(ctx context.Context, id uuid.UUID, fn func(*Order) error) error
}type OrderPlaced struct {
orderID uuid.UUID
total Money
occurredAt time.Time
}
func (o *Order) PullEvents() []Event {
events := o.events
o.events = nil
return events
}type TransferService struct {
accountRepo account.Repository
}
func (s *TransferService) Transfer(ctx context.Context, from, to uuid.UUID, amount Money) error {
// Load aggregates, validate domain rules, coordinate changes
}type Specification[T any] interface {
IsSatisfiedBy(T) bool
}
func And[T any](specs ...Specification[T]) Specification[T] { /* ... */ }
func Or[T any](specs ...Specification[T]) Specification[T] { /* ... */ }
func Not[T any](spec Specification[T]) Specification[T] { /* ... */ }SetStatus()Place()Cancel()Ship()Reconstruct()var ErrNotFound = errors.New(...)references/