Прежде чем обращаться к внешнему API, нужно определить, как данные будут представлены внутри приложения. Доменные модели описывают «что» хранится (температура, город, время), но не «откуда» и «как» данные получены. Если завтра вместо Open-Meteo понадобится другой погодный сервис — модели останутся прежними, изменится только адаптер.
Также на этом этапе нужно научить приложение сохранять настройки между запусками. Город по умолчанию будет храниться в YAML-файле — при следующем запуске программа подхватит его автоматически.
— структура данных, описывающая бизнес-сущность приложения. Она не содержит логики получения данных, не знает про HTTP или JSON. Это внутренний контракт: любой провайдер погоды заполняет одну и ту же структуру.
type Weather struct { City string Temperature float64 Condition string UpdatedAt time.Time }
💡 Gotcha: Не добавляйте
json-теги в доменные модели. JSON-теги нужны только структурам, которые непосредственно десериализуются из ответа API. Доменные модели заполняются вручную в коде маппинга.
— формат хранения настроек, удобный для чтения и редактирования. Пакет gopkg.in/yaml.v3 позволяет маппить YAML-поля на поля структуры через теги.
type Config struct { DefaultCity string `yaml:"default_city"` } data, _ := os.ReadFile("config.yaml") var cfg Config yaml.Unmarshal(data, &cfg)
💡 Gotcha: При первом запуске файла конфигурации не существует. Ошибку
os.ErrNotExistнужно обрабатывать отдельно — возвращать пустойConfig, а не ошибку.
| Пакет | Назначение | Ключевые функции |
|---|---|---|
gopkg.in/yaml.v3 | Чтение/запись YAML | Unmarshal, Marshal |
os | Работа с файлами | ReadFile, WriteFile |
errors | Проверка типа ошибки | Is |
Определить внутреннее представление данных и механизм хранения настроек.
Что необходимо сделать:
Пакет internal/domain:
Структура Today — текущая погода:
City (string) — название городаTemperatureC (float64) — температура в °CFeelsLikeC (float64) — ощущаемая температураCondition (string) — описание условийWindSpeedMS (float64) — скорость ветра (м/с)WindDirectionDeg (int) — направление ветра (градусы)HumidityPercent (int) — влажность (%)PressureHPa (int) — давление (гПа)VisibilityKm (float64) — видимость (км)PrecipitationMm (float64) — осадки за час (мм)UpdatedAt (time.Time) — время обновленияСтруктура HourlyEntry — запись почасового прогноза:
Time (time.Time) — временная меткаTemperatureC (float64) — температураPOPPercent (int) — вероятность осадков (Probability of Precipitation)WindSpeedMS (float64) — скорость ветраСтруктура DailyEntry — запись дневного прогноза:
Date (time.Time) — датаTempMinC (float64) — минимальная температураTempMaxC (float64) — максимальная температураPOPPercent (int) — вероятность осадковWindSpeedMS (float64) — скорость ветраCondition (string) — описание условийПакет internal/config:
Структура Config — настройки приложения:
DefaultCity (string) — город по умолчанию, с yaml-тегом default_cityФункция:
func Load() (Config, error)
Логика:
Config без ошибкиФункция:
func Save(cfg Config) error
Логика:
Config в YAML$ cat config.yaml default_city: "Novosibirsk" $ go run main.go Default city: Novosibirsk
При отсутствии файла конфигурации:
$ go run main.go Default city:
Today в пакете internal/domain содержит поля City string, TemperatureC float64, FeelsLikeC float64, Condition string, WindSpeedMS float64, HumidityPercent int, UpdatedAt time.TimeHourlyEntry и DailyEntry определены в пакете internal/domain с полями для временных меток типа time.TimeConfig в пакете internal/config содержит поле DefaultCity string с yaml-тегомLoad() (Config, error) в пакете internal/config обрабатывает отсутствие файла через errors.Is и возвращает пустой Config без ошибкиSave(cfg Config) error создана в пакете internal/config