# Models

## Overview

Models represent domain entities with their data and business rules. They define the core structure of your application's data and relationships.

## Basic Model Structure

### Standard Entity Pattern
```go
package mdl

import "time"

type Garden struct {
    // Standard fields
    Id        string    `json:"id"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
    
    // Domain-specific fields
    Name        string `json:"name"`
    Description string `json:"description"`
    Location    string `json:"location"`
    UserId      string `json:"user_id"`
    Active      bool   `json:"active"`
    
    // Relationships (populated by Hydrators)
    User        *User         `json:"user,omitempty"`
    Plants      []*Plant      `json:"plants,omitempty"`
    GardenTasks []*GardenTask `json:"garden_tasks,omitempty"`
}

// Required method for Entity interface
func (this *Garden) GetId() string {
    return this.Id
}
```

### Field Types
```go
type Plant struct {
    Id          string     `json:"id"`
    CreatedAt   time.Time  `json:"created_at"`
    UpdatedAt   time.Time  `json:"updated_at"`
    
    // String fields
    Name        string     `json:"name"`
    Species     string     `json:"species"`
    
    // Numeric fields
    Height      float64    `json:"height"`
    Age         int        `json:"age"`
    
    // Boolean fields
    Flowering   bool       `json:"flowering"`
    Edible      bool       `json:"edible"`
    
    // Date fields
    PlantedAt   *time.Time `json:"planted_at,omitempty"`
    HarvestedAt *time.Time `json:"harvested_at,omitempty"`
    
    // Foreign keys
    GardenId    string     `json:"garden_id"`
    VarietyId   *string    `json:"variety_id,omitempty"` // Optional
}
```

## Model Relationships

### One-to-Many Relationships
```go
type Garden struct {
    Id     string   `json:"id"`
    Name   string   `json:"name"`
    UserId string   `json:"user_id"` // Foreign key
    
    // Relationship - loaded by Hydrator
    Plants []*Plant `json:"plants,omitempty"`
}

type Plant struct {
    Id       string `json:"id"`
    Name     string `json:"name"`
    GardenId string `json:"garden_id"` // Foreign key
    
    // Relationship - loaded by Hydrator
    Garden *Garden `json:"garden,omitempty"`
}
```

### Many-to-One Relationships
```go
type PlantTask struct {
    Id         string `json:"id"`
    TaskName   string `json:"task_name"`
    PlantId    string `json:"plant_id"`    // Foreign key
    AssigneeId string `json:"assignee_id"` // Foreign key
    
    // Relationships
    Plant    *Plant `json:"plant,omitempty"`
    Assignee *User  `json:"assignee,omitempty"`
}
```

### Optional Relationships
```go
type Plant struct {
    Id        string  `json:"id"`
    Name      string  `json:"name"`
    VarietyId *string `json:"variety_id,omitempty"` // Optional foreign key
    
    // Optional relationship
    Variety *PlantVariety `json:"variety,omitempty"`
}
```

## Model Validation

### Business Rules in Models
```go
func (this *Garden) Validate() error {
    if this.Name == "" {
        return errors.New("garden name is required")
    }
    
    if len(this.Name) > 100 {
        return errors.New("garden name cannot exceed 100 characters")
    }
    
    if this.UserId == "" {
        return errors.New("garden must have an owner")
    }
    
    return nil
}

func (this *Plant) CanBeHarvested() bool {
    if !this.Edible {
        return false
    }
    
    if this.HarvestedAt != nil {
        return false // Already harvested
    }
    
    // Check if mature enough (example: 90 days)
    if this.PlantedAt != nil && time.Since(*this.PlantedAt) < 90*24*time.Hour {
        return false
    }
    
    return true
}
```

## Model Methods

### Computed Properties
```go
func (this *Garden) GetPlantCount() int {
    return len(this.Plants)
}

func (this *Garden) GetActiveTaskCount() int {
    count := 0
    for _, task := range this.GardenTasks {
        if !task.Completed {
            count++
        }
    }
    return count
}

func (this *Plant) GetAgeInDays() int {
    if this.PlantedAt == nil {
        return 0
    }
    return int(time.Since(*this.PlantedAt).Hours() / 24)
}
```

### Status Methods
```go
func (this *Garden) IsActive() bool {
    return this.Active
}

func (this *Plant) IsMature() bool {
    return this.GetAgeInDays() >= 60 // Example maturity
}

func (this *GardenTask) IsOverdue() bool {
    return !this.Completed && this.DueDate.Before(time.Now())
}
```

## JSON Serialization

### Custom JSON Tags
```go
type Garden struct {
    Id          string    `json:"id"`
    Name        string    `json:"name"`
    Description string    `json:"description"`
    CreatedAt   time.Time `json:"created_at"`
    UpdatedAt   time.Time `json:"updated_at"`
    
    // Omit sensitive data from JSON
    InternalNotes string `json:"-"`
    
    // Conditional inclusion
    User   *User   `json:"user,omitempty"`
    Plants []*Plant `json:"plants,omitempty"`
}
```

### Custom JSON Methods
```go
func (this *Garden) MarshalJSON() ([]byte, error) {
    type Alias Garden
    return json.Marshal(&struct {
        *Alias
        PlantCount int `json:"plant_count"`
    }{
        Alias:      (*Alias)(this),
        PlantCount: this.GetPlantCount(),
    })
}
```

## Model Factory Patterns

### Constructor Functions
```go
func NewGarden(name, description, userId string) *Garden {
    now := time.Now()
    return &Garden{
        Id:          generateID(),
        Name:        name,
        Description: description,
        UserId:      userId,
        Active:      true,
        CreatedAt:   now,
        UpdatedAt:   now,
    }
}

func NewPlant(name, species, gardenId string) *Plant {
    now := time.Now()
    return &Plant{
        Id:        generateID(),
        Name:      name,
        Species:   species,
        GardenId:  gardenId,
        Flowering: false,
        CreatedAt: now,
        UpdatedAt: now,
    }
}
```

## Model Collections

### Collection Methods
```go
type Gardens []*Garden

func (gardens Gardens) FilterActive() Gardens {
    var active Gardens
    for _, garden := range gardens {
        if garden.IsActive() {
            active = append(active, garden)
        }
    }
    return active
}

func (gardens Gardens) FindByUserId(userId string) Gardens {
    var userGardens Gardens
    for _, garden := range gardens {
        if garden.UserId == userId {
            userGardens = append(userGardens, garden)
        }
    }
    return userGardens
}

func (gardens Gardens) TotalPlantCount() int {
    total := 0
    for _, garden := range gardens {
        total += garden.GetPlantCount()
    }
    return total
}
```

## Model Testing

### Unit Testing Models
```go
func TestGarden_Validate(t *testing.T) {
    // Valid garden
    garden := &Garden{
        Name:   "Test Garden",
        UserId: "user123",
    }
    
    err := garden.Validate()
    assert.NoError(t, err)
    
    // Invalid garden - no name
    garden.Name = ""
    err = garden.Validate()
    assert.Error(t, err)
    assert.Contains(t, err.Error(), "name is required")
}

func TestPlant_CanBeHarvested(t *testing.T) {
    plantedDate := time.Now().AddDate(0, 0, -100) // 100 days ago
    
    plant := &Plant{
        Edible:    true,
        PlantedAt: &plantedDate,
    }
    
    assert.True(t, plant.CanBeHarvested())
    
    // Mark as harvested
    harvestedDate := time.Now()
    plant.HarvestedAt = &harvestedDate
    
    assert.False(t, plant.CanBeHarvested())
}
```

## LLM Model Development Notes

- Models represent domain entities with data and behavior
- Include standard Id, CreatedAt, UpdatedAt fields
- Use pointer types for optional fields (*time.Time, *string)
- Implement GetId() method for Entity interface
- Define relationships but let Hydrators populate them
- Add business logic methods for domain rules
- Use JSON tags appropriately (omitempty, -)
- Test model validation and business logic methods
- Keep models focused on data and domain rules