Spec-Zone.ru › Elixir 1.16

Исходный код Зависимости и проекты-зонтики

В этой главе мы обсудим, как управлять зависимостями в Mix.

Наш kv приложение завершено, поэтому пришло время реализовать сервер, который будет обрабатывать запросы, определённые в первой главе:

CREATE shopping
OK

PUT shopping milk 1
OK

PUT shopping eggs 3
OK

GET shopping milk
1
OK

DELETE shopping eggs
OK

Однако вместо добавления большего кода в kv приложение, мы собираемся создать TCP-сервер как другое приложение, которое является клиентом kv приложение. Поскольку весь рантайм и экосистема Elixir ориентированы на приложения, имеет смысл разделить наши проекты на более мелкие приложения, которые работают вместе, а не создавать одно большое монолитное приложение.

Перед созданием нового приложения мы должны обсудить, как Mix обрабатывает зависимости. На практике мы обычно работаем с двумя типами зависимостей: внутренними и внешними. Mix поддерживает механизмы работы с обеими.

Внешние зависимости

Внешние зависимости — это те, которые не связаны с вашей предметной областью. Например, если вам нужен HTTP-API для вашего распределённого приложения KV, вы можете использовать проект Plug в качестве внешней зависимости.

Установка внешних зависимостей проста. Чаще всего мы используем менеджер пакетов Hex, перечислив зависимость в функции deps в нашем mix.exs файле:

def deps do
  [{:plug, "~> 1.0"}]
end

Эта зависимость относится к последней версии Plug в серии версий 1.x.x, которая была добавлена в Hex. Это обозначается префиксом ~> перед номером версии. Дополнительную информацию о задании требований к версиям см. в документации для модуля Version.

Обычно стабильные релизы добавляются в Hex. Если вы хотите зависеть от внешней зависимости, которая всё ещё находится в разработке, Mix может управлять зависимостями Git:

def deps do
  [{:plug, git: "https://github.com/elixir-lang/plug.git"}]
end

Вы заметите, что при добавлении зависимости в ваш проект Mix генерирует mix.lock файл, который гарантирует повторяемость сборки. Файл блокировки должен быть добавлен в вашу систему управления версиями, чтобы гарантировать, что все, кто использует проект, будут использовать те же версии зависимостей, что и вы.

Mix предоставляет множество задач для работы с зависимостями, которые можно найти в mix help:

$ mix help
mix deps              # Lists dependencies and their status
mix deps.clean        # Deletes the given dependencies' files
mix deps.compile      # Compiles dependencies
mix deps.get          # Gets all out of date dependencies
mix deps.tree         # Prints the dependency tree
mix deps.unlock       # Unlocks the given dependencies
mix deps.update       # Updates the given dependencies

Наиболее распространёнными задачами являются mix deps.get и mix deps.update. После получения зависимости они автоматически компилируются для вас. Вы можете узнать больше о deps, набрав mix help deps, и в документации для модуля Mix.Tasks.Deps.

Внутренние зависимости

Внутренние зависимости — это те, которые специфичны для вашего проекта. Они обычно не имеют смысла вне контекста вашего проекта/компании/организации. Часто вы хотите сохранить их конфиденциальными, по техническим, экономическим или деловым причинам.

Если у вас есть внутренняя зависимость, Mix поддерживает два метода работы с ними: репозитории Git или проекты-зонтики.

Например, если вы поместите проект kv в репозиторий Git, вам необходимо добавить его в код deps, чтобы использовать его:

def deps do
  [{:kv, git: "https://github.com/YOUR_ACCOUNT/kv.git"}]
end

Однако, если репозиторий приватный, вам может потребоваться указать приватный URL git@github.com:YOUR_ACCOUNT/kv.git. В любом случае Mix сможет его получить, если у вас есть соответствующие учётные данные.

Использование репозиториев Git для внутренних зависимостей в Elixir несколько не рекомендуется. Помните, что рантайм и экосистема Elixir уже предоставляют концепцию приложений. Таким образом, ожидается, что вы часто будете разбивать свой код на приложения, которые могут быть логически организованы, даже в рамках одного проекта.

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

По этой причине Mix поддерживает «проекты-зонтики». Проекты-зонтики используются для создания приложений, которые работают вместе в одном репозитории. Именно этот стиль мы собираемся изучить в следующих разделах.

Давайте создадим новый проект Mix. Мы творчески назовём его kv_umbrella, и этот новый проект будет содержать существующее kv приложение и новое kv_server приложение внутри. Структура каталогов будет выглядеть так:

+ kv_umbrella
  + apps
    + kv
    + kv_server

Интересно, что Mix имеет множество удобств для работы с такими проектами, например, возможность компилировать и тестировать все приложения внутри apps одной командой. Однако, даже если все они перечислены вместе внутри apps, они всё ещё декомпозированы друг от друга, поэтому вы можете собирать, тестировать и развертывать каждое приложение по отдельности, если захотите.

Итак, приступим!

Проекты-зонтики

Начнём новый проект с помощью mix new. Этот новый проект будет называться kv_umbrella и нам нужно передать опцию --umbrella при его создании. Не создавайте этот новый проект внутри существующего kv проекта!

$ mix new kv_umbrella --umbrella
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating apps
* creating config
* creating config/config.exs

Из выведенной информации можно увидеть, что генерируется гораздо меньше файлов. Сгенерированный mix.exs файл тоже отличается. Давайте посмотрим (комментарии удалены):

defmodule KvUmbrella.MixProject do
  use Mix.Project

  def project do
    [
      apps_path: "apps",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  defp deps do
    []
  end
end

Что отличает этот проект от предыдущего, так это запись apps_path: "apps" в определении проекта. Это означает, что этот проект будет работать как зонтик. Такие проекты не имеют исходных файлов ни тестов, хотя они могут иметь свои собственные зависимости. Каждое дочернее приложение должно быть определено внутри каталога apps.

Давайте перейдём в каталог apps и начнём создавать kv_server. На этот раз мы собираемся передать флаг --sup , который скажет Mix сгенерировать древовидную структуру для контроля параллельных задач автоматически, вместо того, чтобы создавать её вручную, как мы делали в предыдущих главах:

$ cd kv_umbrella/apps
$ mix new kv_server --module KVServer --sup

Сгенерированные файлы похожи на те, что мы сгенерировали для kv, с некоторыми отличиями. Давайте откроем mix.exs:

defmodule KVServer.MixProject do
  use Mix.Project

  def project do
    [
      app: :kv_server,
      version: "0.1.0",
      build_path: "../../_build",
      config_path: "../../config/config.exs",
      deps_path: "../../deps",
      lockfile: "../../mix.lock",
      elixir: "~> 1.14",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications
  def application do
    [
      extra_applications: [:logger],
      mod: {KVServer.Application, []}
    ]
  end

  # Run "mix help deps" to learn about dependencies
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
      # {:sibling_app_in_umbrella, in_umbrella: true},
    ]
  end
end

Во-первых, так как мы сгенерировали этот проект внутри kv_umbrella/apps, Mix автоматически определил структуру зонтика и добавил четыре строки в определение проекта:

build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",

Эти опции означают, что все зависимости будут извлечены в kv_umbrella/deps, и они будут совместно использовать одни и те же файлы сборки, конфигурации и блокировки. Мы ещё не говорили о конфигурации, но из этого можно сделать вывод, что все конфигурации и зависимости совместно используются всеми проектами в зонтике, а не по отдельным приложениям.

Второе изменение — в функции application внутри mix.exs:

def application do
  [
    extra_applications: [:logger],
    mod: {KVServer.Application, []}
  ]
end

Поскольку мы передали флаг --sup , Mix автоматически добавил mod: {KVServer.Application, []}, указав, что KVServer.Application — это модуль обратного вызова нашего приложения. KVServer.Application запустит древовидную структуру контроля параллельных задач нашего приложения.

Фактически, давайте откроем lib/kv_server/application.ex:

defmodule KVServer.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  @impl true
  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Starts a worker by calling: KVServer.Worker.start_link(arg)
      # {KVServer.Worker, arg},
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: KVServer.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Обратите внимание, что он определяет функцию обратного вызова приложения, start/2, и вместо определения надсмотрщика под названием KVServer.Supervisor , использующего модуль Supervisor, он удобно определил надсмотрщика внутри! Вы можете узнать больше о таких надсмотрщиках, прочитав документацию модуля Supervisor.

Мы уже можем попробовать наше первое дочернее приложение зонтика. Мы могли бы запустить тесты внутри каталога apps/kv_server , но это не очень интересно. Вместо этого перейдите в корень проекта зонтика и запустите mix test:

$ mix test

И это работает!

Поскольку мы хотим, чтобы kv_server в конечном итоге использовал функциональность, которую мы определили в kv, нам нужно добавить kv в качестве зависимости к нашему приложению.

Зависимости внутри проекта-зонтика

Зависимости между приложениями в проекте-зонтике всё ещё должны быть явно определены, и Mix делает это легко. Откройте apps/kv_server/mix.exs и измените функцию deps/0 на следующее:

defp deps do
  [{:kv, in_umbrella: true}]
end

Строка выше делает :kv доступной как зависимость внутри :kv_server и автоматически запускает приложение :kv перед запуском сервера.

Наконец, скопируйте kv приложение, которое мы создали до сих пор, в каталог apps в нашем новом проекте зонтика. Конечная структура каталогов должна соответствовать структуре, о которой мы упоминали ранее:

+ kv_umbrella
  + apps
    + kv
    + kv_server

Теперь нам нужно изменить apps/kv/mix.exs , чтобы оно содержало записи зонтика, которые мы видели в apps/kv_server/mix.exs. Откройте apps/kv/mix.exs и добавьте в функцию project/0:

build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",

Теперь вы можете запустить тесты для обоих проектов из корня проекта зонтика с помощью mix test. Отлично!

Не увлекайтесь

Проекты-зонтики — это удобство, которое помогает организовать и управлять несколькими приложениями. Хотя он обеспечивает определённую степень разделения между приложениями, эти приложения не полностью декомпозированы, так как они используют общую конфигурацию и зависимости.

Шаблон хранения нескольких приложений в одном репозитории известен как «моно-репозиторий». Проекты-зонтики максимизируют этот шаблон, предоставляя удобства для компиляции, тестирования и выполнения нескольких приложений одновременно.

Если вы обнаружите, что вам нужно использовать разные конфигурации для одной и той же зависимости в каждом приложении или использовать разные версии зависимостей, вероятно, ваш код вырос за пределы возможностей зонтиков.

Хорошая новость заключается в том, что разделить зонтик довольно просто, так как вам просто нужно переместить приложения за пределы каталога apps/ проекта зонтика и обновить файл mix.exs проекта, чтобы больше не устанавливать конфигурации build_path, config_path, deps_path, и lockfile. Вы можете зависеть от частных проектов за пределами зонтика несколькими способами:

  1. Переместите его в отдельную папку в одном репозитории и укажите на него с помощью зависимости по пути (шаблон моно-репозитория)
  2. Переместите репозиторий в отдельный репозиторий Git и зависьте от него
  3. Опубликуйте проект в частной организации Hex.pm

Подводя итоги

В этой главе мы узнали больше о зависимостях Mix и проектах-зонтиках. Хотя мы можем запустить kv без сервера, наш kv_server напрямую зависит от kv. Разделив их на отдельные приложения, мы получаем больший контроль над их разработкой и тестированием.

При использовании приложений-зонтиков важно иметь четкую границу между ними. Наше грядущее kv_server должно получать доступ только к общедоступным API, определённым в kv. Представьте себе приложения-зонтики как любые другие зависимости или даже сам Elixir: вы можете получить доступ только к тому, что является общедоступным и документированным. Обращение к приватной функциональности в ваших зависимостях — плохая практика, которая в конечном итоге приведёт к поломке вашего кода при обновлении до новой версии.

Приложения-зонтики также могут служить промежуточным этапом для последующего выделения приложения из вашего кода. Например, представьте веб-приложение, которое должно отправлять «push-уведомления» своим пользователям. Весь «система push-уведомлений» может быть разработан как отдельное приложение в зонтике со своей собственной древовидной структурой управления и API. Если в какой-то момент другому проекту потребуется система push-уведомлений, её можно переместить в частный репозиторий или пакет Hex.

Наконец, имейте в виду, что все приложения в проекте-зонтике используют одинаковые конфигурации и зависимости. Если два приложения в вашем зонтике должны настраивать одну и ту же зависимость совершенно разными способами или даже использовать разные версии, вероятно, вы вышли за рамки преимуществ, предоставляемых зонтиками. Помните, что вы можете разбить зонтик и всё равно воспользоваться преимуществами «моно-репозиториев».

После того, как наш проект-зонтик запущен, пришло время начать писать наш сервер.

← Предыдущая страница Ускорение с ETS
Следующая страница → Задачи и gen_tcp

Загрузить версию ePub

Создано с помощью ExDoc (v0.32.2) для языка программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.16.3/dependencies-and-umbrella-projects.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API