Spec-Zone.ru › Elixir 1.18

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

В этой главе мы обсудим, как управлять зависимостями в 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 Package Manager, указав зависимость внутри функции 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. Отлично!

Не пейте колу

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

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

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

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

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

Подведение итогов

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

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

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

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

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

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

Скачать версию ePub

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

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

Spec-Zone.ru

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