Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Mimir Architecture

This document provides a high-level overview of the Mimir codebase architecture for developers.

Crate Overview

Mimir is organized as a Cargo workspace with 4 crates:

┌─────────────────────────────────────────────────────────┐
│                    mimir (Main App)                      │
│                   Tauri Desktop Application              │
└─────────────────────────┬───────────────────────────────┘
                          │
        ┌─────────────────┼─────────────────┐
        ▼                 ▼                 ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│  mimir-core   │ │  mimir-mcp    │ │  mimir-print  │
│  Core Logic   │ │  MCP Server   │ │  PDF Export   │
└───────────────┘ └───────────────┘ └───────────────┘

mimir-core

The heart of the system containing:

  • Models: Domain types for campaigns, characters, D&D catalog data
  • Services: Business logic (28 services)
  • DAL: Data Access Layer with repository traits
  • Migrations: 35 Diesel migrations for SQLite schema

mimir-mcp

MCP (Model Context Protocol) server for Claude Code integration:

  • Campaign, module, character, and catalog tools
  • Uses DAL directly for database access

mimir-print

PDF generation using Typst:

  • Character sheets
  • Campaign documents
  • Markdown to Typst conversion

mimir

Tauri desktop application:

  • Commands: 50+ Tauri command handlers
  • Services: LLM integration, context management
  • State: Consolidated AppState struct

Request Flow

┌──────────┐    ┌─────────────┐    ┌─────────────┐    ┌──────────┐
│ Frontend │───▶│   Tauri     │───▶│   Service   │───▶│ Database │
│ (Vue 3)  │    │  Command    │    │   Layer     │    │ (SQLite) │
└──────────┘    └─────────────┘    └─────────────┘    └──────────┘
     ▲                │                   │
     │                ▼                   │
     │         ┌─────────────┐           │
     └─────────│  AppState   │◀──────────┘
               │  (db, paths) │
               └─────────────┘

Typical Command Flow

  1. Frontend invokes Tauri command (e.g., get_campaign)
  2. Tauri Command extracts State<AppState> and parameters
  3. Service performs business logic, accesses database
  4. Response returned to frontend as JSON

Example:

#![allow(unused)]
fn main() {
#[tauri::command]
pub async fn get_campaign(
    campaign_id: i32,
    state: State<'_, AppState>,
) -> Result<ApiResponse<Campaign>, ApiError> {
    let mut conn = state.db.get_connection()?;
    let campaign = CampaignService::get_by_id(&mut conn, campaign_id)?;
    Ok(ApiResponse::success(campaign))
}
}

Database Design

See ADR-0001 for denormalized design rationale.

Key Design Decisions

  • Denormalized: Child entities (subclasses, subraces) contain full parent data
  • JSON Columns: Complex/variable data stored as JSON (see ADR-0003)
  • Multi-Ruleset: Content organized by rule system and source (see ADR-0002)

Schema Overview (37 tables)

Campaign Management:

  • campaigns, modules, sessions
  • documents, template_documents
  • workflow_cards

D&D Catalog:

  • catalog_* tables for each entity type (spells, monsters, items, etc.)
  • uploaded_books, catalog_sources

Characters:

  • players, characters, character_versions
  • campaign_players (links players to campaigns)

Service Layer

Services follow the stateful pattern per ADR-0005:

#![allow(unused)]
fn main() {
pub struct SpellService<'a> {
    pub conn: &'a mut SqliteConnection,
}

impl<'a> SpellService<'a> {
    pub fn new(conn: &'a mut SqliteConnection) -> Self {
        Self { conn }
    }

    pub fn search(&mut self, filters: SpellFilters) -> Result<Vec<SpellSummary>> {
        // Query logic
    }
}
}

Service Categories

CategoryServicesPurpose
CampaignCampaignService, ModuleService, SessionServiceCampaign management
CatalogSpellService, MonsterService, ItemService, etc.D&D reference data
CharacterCharacterService, CharacterCreationServicePC/NPC management
ContentDocumentService, TemplateServiceDocument management

Key Patterns

Error Handling

#![allow(unused)]
fn main() {
// Application-level errors
pub enum MimirError {
    Database(DbError),
    Print(String),
    Llm(String),
    NotFound(String),
    // ...
}

// Database-level errors
pub enum DbError {
    NotFound { entity_type, id },
    ConstraintViolation { field, message },
    Query(diesel::result::Error),
    // ...
}
}

State Management

AppState consolidates all shared state:

#![allow(unused)]
fn main() {
pub struct AppState {
    pub db: Arc<DatabaseService>,       // Database connection pool
    pub paths: Arc<AppPaths>,           // Application paths
}
}

Directory Structure

crates/
├── mimir/                    # Main Tauri app
│   ├── src/
│   │   ├── main.rs             # Entry point
│   │   ├── state.rs            # AppState
│   │   ├── commands/           # Tauri commands
│   │   │   ├── catalog/        # D&D reference commands
│   │   │   ├── campaign/       # Campaign management
│   │   │   ├── character/      # Character operations
│   │   │   └── chat/           # LLM chat commands
│   │   └── services/           # Business logic
│   │       ├── llm/            # LLM integration
│   │       └── tools/          # LLM tool implementations
│   └── frontend/               # Vue 3 frontend
│
├── mimir-core/              # Core business logic
│   ├── src/
│   │   ├── models/             # Domain models
│   │   │   ├── catalog/        # D&D entity types
│   │   │   └── campaign/       # Campaign types
│   │   ├── services/           # Business logic services
│   │   ├── dal/                # Data access layer
│   │   └── migrations/         # Database migrations
│
├── mimir-mcp/                  # MCP server for Claude Code
│   ├── src/
│   │   ├── context.rs          # Database context
│   │   └── tools/              # MCP tool handlers
│   └── plugin/                 # Claude Code plugin definition
│
└── mimir-print/                # PDF generation
    └── src/
        └── sections/           # PDF section renderers

Development Guidelines

Adding a New Catalog Entity

  1. Create model in mimir-core/src/models/catalog/
  2. Add migration in mimir-core/migrations/
  3. Create service in mimir-core/src/services/
  4. Add Tauri commands in mimir/src/commands/catalog/
  5. Update schema.rs after migration

Testing

# Run all tests
cargo test --workspace

# Run core tests only
cargo test -p mimir-core

# Run specific test
cargo test test_name