Анализ получившейся архитектуры кода

Давайте посмотрим свежим взглядом на получившуюся архитектуру кода.

Мы имеем 4 слоя приложения:

  1. Модуль weather, запускающий приложение и связывающий остальные слои. Важно обратить внимание: этот файл не содержит никакой логики реализации, никакой бизнес-логики. Это точка входа в приложение. Она не знает ничего о деталях реализации всех остальных нижележащих слоёв приложения.

  2. Модуль coordinates отвечает за получение координат из внешней команды whereami. Сюда инкапсулирована вся логика по работе с координатами. Эта логика ничего не знает о том, для чего эти координаты будут использованы затем в приложении. Модуль определяет структуру данных для хранения и передачи в приложение координат.

    Если нам понадобится получать координаты откуда-то иначе — мы перепишем логику этого модуля, никак не затронув все остальные модули приложения. Связь этого модуля с остальными — слабая, и это хорошо.

  3. Модуль weather_api_service инкапсулирует в себе логику получения погоды по координатам. Он не знает, откуда были получены координаты, поступившие в этот модуль. Он не знает, как будет использоваться погода дальше в приложении. В этом модуле определена структура для хранения и передачи данных погоды в приложение.

    Если нам понадобится получать погоду в другом API-сервисе — мы заменим логику этого модуля и это никак не затронет остальные модули приложения. Связь этого модуля с остальными — слабая, и это хорошо.

  4. Модуль weather_formatter отвечает за преобразование погоды в строку. Он ничего не знает о том, откуда погода была получена, была ли она получена по координатам GPS или по названию населённого пункта или как-то иначе, он не знает ничего кроме того, как преобразовать данные погоды в строку, всё. Связь этого модуля с остальными слабая, и это хорошо. В любой момент мы можем изменить логику форматирования данных погоды (добавив в неё иконки погоды, например), никак не затронув при этом все остальные модули приложения.

  5. Модуль history инкапсулирует в себе логику по сохранению истории погоды. Этот модуль также независим от остальных модулей. Более того, реализована гибкая схема смены хранилища на любое другое через механизм интерфейсов. Ответственность за то, какое хранилище будет использовано для сохранения данных, лежит вовне этого модуля. Можно, например, данные погоды днём сохранять в текстовый плоский txt-файл, а данные ночной погоды — в JSON. Для этого не придётся ничего менять в самом модуле history. Чем меньше поводов менять код какого-то модуля, класса, функции — тем лучше.

Получается, что мы реализовали всё приложение в виде слабозависимых друг от друга модулей. При этом эти модули могут использоваться и в составе других приложений, они reusable, то есть переиспользуемые. Скажем, модуль получения GPS-координат может использоваться в программе вычисления расстояния от текущей точки, где мы находимся, до Рима. Почему нет. Для этого не понадобится изменять этот модуль. Отлично!

Если мы откроем код любого модуля, любой функции — нам сразу станет понятно, какие данные принимаются на вход и какие возвращаются на выход, причём понятно максимально точно. Это:

  1. Облегчает чтение кода — все типы данных в явном виде и максимально конкретно прописаны, не надо их предугадывать.
  2. Гарантируется отсутствие ошибок использования типов — IDE подсветит, если мы что-то используем не так, как нужно; также на ошибки укажет инструмент статического анализа вроде mypy, о котором мы поговорим ниже.
  3. IDE поможет писать код всем, использующим наши разработанные модули. Будет работать автодополнение по полям и методам классов с учетом типов, которые мы указали.

Финальный вариант исходного кода программы размещён на Github: https://github.com/alexey-goloburdin/weather