В архитектуре Mapbox GL JS система «сервисов» представляет собой совокупность внутренних подсистем, каждая из которых отвечает за строго определённый участок работы: загрузку данных, управление стилем, отрисовку, обработку событий, работу с источниками данных и взаимодействие с WebGL-рендерером. Эти компоненты не существуют изолированно — они связаны через принципы инверсии управления и внедрения зависимостей, где центральный объект карты выступает контейнером контекста и поставщиком зависимостей.
В основе Mapbox GL JS лежит модульная архитектура, где функциональность разбита на сервисные домены:
Map,
Transform)Style, StyleLayer,
StyleImage)SourceCache,
GeoJSONSource, VectorTileSource)Painter, Program,
Shader)RequestManager,
ResourceLoader)WorkerPool,
TileWorker)Evented)Каждый из этих компонентов можно рассматривать как сервис, предоставляющий набор функций и зависящий от других сервисов через явно переданные или контекстные зависимости.
Ключевая идея заключается в том, что ни один компонент не создаётся в полной изоляции — он получает необходимые зависимости от окружения карты.
Объект карты (Map) играет роль центрального контейнера,
который агрегирует и распределяет зависимости. При инициализации он
создаёт и связывает все ключевые сервисы:
Transform)Style)RequestManager)TileManager)Painter)Каждый сервис получает доступ к общему состоянию через ссылку на карту или через переданные при создании параметры.
Такой подход можно рассматривать как упрощённую форму dependency
injection container, где Map выступает в роли root
provider.
Сервис Style отвечает за интерпретацию Mapbox Style
Specification и преобразование декларативного описания карты в набор
исполняемых структур.
Он управляет:
layers)sources)sprite)glyphs)images)Style не выполняет рендеринг напрямую, но формирует
промежуточное представление, которое затем потребляется рендерером.
Внутри архитектуры он зависит от:
Таким образом, Style является сервисом-оркестратором,
координирующим работу других подсистем.
SourceCache управляет жизненным циклом источников данных
и их тайлов. Он инкапсулирует:
Каждый источник (VectorTileSource,
RasterSource, GeoJSONSource) внедряется в
систему через SourceCache, который выступает посредником
между стилем и рендерером.
Зависимости источника включают:
Таким образом реализуется слабая связность между источниками и рендеринговым ядром.
Одной из ключевых особенностей Mapbox GL JS является использование Web Workers для тяжёлых вычислений:
Worker-сервис (WorkerPool, TileWorker)
получает «внедрённый» контекст в виде сериализованных сообщений. Это
форма dependency injection через message passing.
Вместо прямых зависимостей используется контракт:
Таким образом, зависимости «передаются» не через ссылки, а через структурированные сообщения.
Сервис Painter отвечает за финальный этап — отрисовку
WebGL-сцен.
Он зависит от:
Style (что рисовать)Transform (где рисовать)SourceCache (какие данные доступны)TextureManager (текстуры и изображения)Painter не знает ничего о загрузке данных или сетевых
запросах — он работает только с уже подготовленным состоянием.
Внутри него реализуется строгая инверсия зависимостей: вместо того чтобы самому запрашивать данные, он получает их в готовом виде через слой стиля и кэша.
Evented — базовый механизм событий в Mapbox GL JS —
также является сервисом, внедряемым во многие компоненты.
Он обеспечивает:
Практически каждый сервис зависит от event bus, но не реализует его самостоятельно. Это предотвращает циклические зависимости и позволяет централизовать управление состоянием.
Хотя Mapbox GL JS не использует классический dependency injection контейнер (как в Angular или Spring), он реализует аналог через:
map как глобального контекстаcreateStyle,
createPainter, createSource)Пример паттерна:
MapmapЭто создаёт гибридную модель: частично явное DI, частично service locator.
Сервисы образуют направленный граф зависимостей:
Map (root container)
Style
SourceCacheImageManagerPainter
StyleTransformSourceCache
WorkerPoolWorkerPool
RequestManagerЦиклы в этой структуре строго избегаются, поскольку они приводят к невозможности инициализации и неконсистентному состоянию карты.
Mapbox GL JS поддерживает изменение состояния карты во время выполнения:
setStyle)addSource)addLayer,
removeLayer)Каждое такое изменение приводит к:
Это означает, что DI в системе является не статическим, а динамическим: зависимости могут пересоздаваться в runtime.
Расширение функциональности часто реализуется через внешние сервисы:
Такие компоненты внедряются в систему через публичное API карты и получают доступ к внутренним сервисам косвенно:
mapЭто создаёт ограниченную форму внешнего DI, где карта остаётся единственным точкой входа.
Инверсия управления в Mapbox GL JS проявляется в том, что:
Например:
TransformPainterSourceCacheПоток управления всегда идёт сверху вниз от Map, а не от
отдельных модулей.
Несмотря на гибкость, такая архитектура имеет ряд особенностей:
map усложняют анализ
связейMap может затруднять
тестированиеТем не менее такая модель обеспечивает высокую производительность и возможность работы с большими объёмами геоданных в браузере без блокировки UI-потока