Skip to content

Architecture overview

A 60-second mental model for what this suite is and how its parts fit together.

Three layers

  ┌──────────────────────────────────────────────────┐
  │  Blizzard Battle.net                             │
  │    - Game Data APIs  (global, app-token)         │
  │    - Profile APIs    (per-player — see below)    │
  └──────────────────────────────────────────────────┘
              BattleNetClient   (auth, rate limit, retry, caching)
  ┌──────────────────────────────────────────────────┐
  │  Per-domain submodules                           │
  │    - Api layer       wraps endpoints             │
  │    - Sync layer      API → Drupal entities       │
  │    - Dashboard layer admin UI + actions          │
  └──────────────────────────────────────────────────┘
  ┌──────────────────────────────────────────────────┐
  │  Drupal site                                     │
  │    - Taxonomy terms  (reference data)            │
  │    - Content entities (catalogs, characters)     │
  │    - Junction entities (per-owner rows)          │
  │    - Views, Twig, your own custom code           │
  └──────────────────────────────────────────────────┘

Module taxonomy

Every module falls into one of four roles. Understanding which role a module plays tells you what it's allowed to depend on and what lifecycle rules it obeys.

Role Modules Depends on Never depends on
Core wow key, http, state, lock anything WoW
Reference data wow_realm, wow_playable_class, wow_playable_race, wow_playable_specialization, wow_power_type core anything WoW-specific
Catalog + junction wow_achievement, wow_mount, wow_pet, wow_title, wow_toy, wow_reputation, wow_item, wow_creature, wow_quest core, reference data as needed owners (wow_character, wow_guild)
Owner wow_character, wow_guild core + the reference data they project owners projecting OTHER directions
Auth / glue wow_battlenet_login, wow_user OIDC + whichever domains it glues other auth/glue modules

The key rule: catalog modules never know about owners. Owner modules inject their reference via hook_entity_base_field_info at runtime. Enabling / disabling either side is always valid.

See modules for the full per-module breakdown.

Two auth modes

BattleNetClient::get() accepts an optional access-token argument. If omitted, it falls back to the app-scoped token via BattleNetAuth. That means:

  • App token (default) — used for every Game Data call, and today also for the per-character and per-guild Profile endpoints. Blizzard accepts app tokens on those.
  • User OAuth token (opt-in) — only passed explicitly by AccountProfileApi (in wow_user) when calling /profile/user/wow, which requires the user's own consent. wow_battlenet_login captures the token on OIDC login and stores it in user.data.

The endpoints-by-auth split is a property of the call site, not of BattleNetClient. See data flow for the mechanics.

Regions are hardcoded

The five Blizzard regions (US, EU, KR, TW, CN) are infrastructure — they define API hosts, OAuth endpoints, and locale sets, and they never change. Rather than syncing from the /data/wow/region/ endpoint, the suite hardcodes them as the Drupal\wow\Enum\Region PHP enum. This keeps region logic available at boot time (before any API call), encapsulates the CN credential separation (globalCases() excludes CN), and avoids a sync dependency on data that is effectively constant.

Three data shapes

The suite uses three storage strategies, chosen based on how the data behaves upstream:

Reference data → taxonomy terms

Realms, classes, races, specializations, power types, reputation factions. Stable enough that content can reference them via entity_reference, cheap to render inside Views, and Drupal's taxonomy UI is already good enough to browse them.

Catalogs → content entities

Achievements, mounts, pets, titles, toys, items. Large sets, structured fields, need custom storage + queue-based sync. Content entities give us last_fetched, translations, direct entity queries.

Per-owner data → owner-agnostic junction entities

wow_achievement_progress, wow_mount_collection, wow_collected_pet, wow_title_award, wow_toy_collection, wow_reputation_standing. Each is defined without an owner field. wow_character and wow_guild project their owner references onto junctions at runtime via hook_entity_base_field_info.

This lets a site enable only catalogs without paying the owner-tracking cost, and makes the same junction type work for multiple owners (a single wow_achievement_progress schema serves both character AND guild achievements).

See plugin system for how owners get their per-owner rows populated, and lifecycle for how projection lives and dies.

Where to go next

  • Modules — the full submodule inventory with roles and responsibilities
  • Data flow — how bytes move from Blizzard into entities and back out to the UI
  • Plugin system — DashboardSectionProvider and CharacterDataProvider details
  • Lifecycle — install, uninstall, TTL, delete cascades