ADR-003: Monorepo with Five Publishable Packages¶
Status¶
Accepted
Date¶
2024-02-01
Context¶
MAID combines a general-purpose MUD engine with specific gameplay content (classic RPG mechanics, tutorial world) and an auxiliary registry service. These concerns have fundamentally different dependency profiles and release cadences:
- The core engine needs asyncio, Pydantic, FastAPI, and cryptographic libraries, but should not depend on game-specific models like spell lists or weapon types.
- Gameplay content (combat, quests, crafting) needs the engine and standard library but should be independently versioned and replaceable.
- A tutorial world demonstrates patterns but should not be a required dependency.
- A plugin registry service is a standalone web application.
The original codebase was a single maid/ package. As it grew, the boundary
between infrastructure and game content became unclear, making it difficult to
create alternative content packs without forking the entire project.
Decision¶
Organize the project as a UV workspace monorepo (pyproject.toml at root) with
five independently publishable packages:
- maid-engine (
packages/maid-engine/) - Pure infrastructure: ECS, networking (Telnet/WebSocket), authentication, AI provider integration, CLI, plugin system, document store, hot reload, profiling, and theGameEnginetick loop. - maid-stdlib (
packages/maid-stdlib/) - Reusable standard components (PositionComponent,HealthComponent,InventoryComponent,DialogueComponent), base events (RoomEnterEvent,DamageDealtEvent), common commands (look, move, get, drop), and utilities (dice rolling, text formatting). - maid-classic-rpg (
packages/maid-classic-rpg/) - A content pack implementing traditional MUD gameplay: combat with tactical positioning, magic system, crafting, economy, quests, guilds, factions, PvP arenas, weather, and AI-powered NPC dialogue. - maid-registry (
packages/maid-registry/) - A standalone ASGI service for content pack discovery, search, and metadata management. - maid-tutorial-world (
packages/maid-tutorial-world/) - An example content pack with four tutorial areas (village, forest, goblin camp, cave), demonstrating gameplay patterns for content pack authors.
The dependency layering is strictly enforced:
maid-classic-rpg, maid-tutorial-world (depend on maid-stdlib)
maid-stdlib (depends on maid-engine)
maid-engine (no internal deps)
maid-registry (standalone)
Consequences¶
Positive¶
- Clear dependency boundaries: The engine cannot accidentally import RPG combat models. Content packs cannot reach into engine internals. This is enforced by Python's package import system.
- Independent publishing:
maid-enginecan be released withoutmaid-classic-rpg. A community member can createmaid-sci-fi-rpgdepending only onmaid-engineandmaid-stdlib. - Selective installation: Users can install
maid-enginealone for a custom MUD, or install the full stack withmaid[all]from the rootpyproject.toml. - Focused testing:
uv run pytest packages/maid-engine/tests/runs only engine tests. Each package has its own test suite.
Negative¶
- Cross-package development friction: Changes spanning engine and stdlib require coordinating two packages. UV workspace handles this with editable installs, but CI must build and test all packages.
- Import path verbosity: Code references
maid_engine.core.ecs.entity.Entityrather thanmaid_engine.core.ecs.Entity, though packages re-export common symbols in__init__.py. - Duplication risk: Common patterns (like event definitions) can drift between packages if not carefully coordinated through the stdlib.
- Build complexity: The UV workspace in
pyproject.tomlmust correctly resolve inter-package dependencies for development and publishing.
Alternatives Considered¶
Single Package¶
Keeping everything in one maid package was the original approach. Rejected because
it prevented clean separation of engine infrastructure from game content, making it
impossible for third parties to build content packs without depending on the entire
RPG system.
Separate Repositories¶
Splitting into five separate Git repositories was considered. Rejected because it would make cross-package changes extremely difficult (requiring separate PRs, version bumps, and releases for a single logical change), and the packages are tightly co-developed during this phase of the project.
Three Packages (Engine, Content, Registry)¶
A simpler split was considered (engine, all-content, registry). Rejected because it would couple the standard library components with the RPG-specific content, forcing anyone who wants basic MUD commands to also install the full RPG system.