Skip to content

⚠️ Strict Architecture Rules - READ THIS FIRST

🚨 CRITICAL: Layer Hierarchy Must Be Respected

This is not a suggestion. This is a requirement.

CASCADA Framework enforces strict one-way dependency flow. Breaking these rules will:

  • ❌ Break the entire architecture
  • ❌ Make Core systems unusable in other projects
  • ❌ Create circular dependencies
  • ❌ Prevent extracting Core as pnpm package

📐 The Three Layers

┌─────────────────────────────────┐
│  PLATFORM (Base)                │  ← Phaser configuration ONLY
│  • No game logic                │
│  • No UI components             │
│  • Just Phaser setup            │
└─────────────┬───────────────────┘
              │ uses ↓
┌─────────────▼───────────────────┐
│  CORE (Reusable Systems)        │  ← Universal, game-agnostic
│  • UIManager, VueUIBridge       │
│  • GameModeManager              │
│  • ViewportManager              │
│  • Can be extracted as package  │
└─────────────┬───────────────────┘
              │ uses ↓
┌─────────────▼───────────────────┐
│  GAME (Your Game Logic)         │  ← Specific to LINX or your game
│  • LINX territory logic         │
│  • Game-specific scenes         │
│  • Game-specific UI             │
└─────────────────────────────────┘

Dependency flow is ONE-WAY only:

GAME → uses CORE → uses PLATFORM

✅ ALLOWED Import Patterns

In PLATFORM Layer

typescript
// ✅ ALLOWED - External dependencies
import Phaser from "phaser";
import { Game } from "phaser";

// ❌ FORBIDDEN - Cannot import from Core
import { UIManager } from "@core/ui/UIManager"; // ❌ NO!

// ❌ FORBIDDEN - Cannot import from Game
import { TerritoryScene } from "@game/scenes/TerritoryScene"; // ❌ NO!

PLATFORM knows nothing about Core or Game existence.


In CORE Layer

typescript
// ✅ ALLOWED - External dependencies
import Phaser from "phaser";
import { reactive } from "vue";

// ✅ ALLOWED - Platform imports
import { baseConfig } from "@platform/base.config";

// ✅ ALLOWED - Other Core modules
import { UIManager } from "@core/ui/UIManager";
import { GameModeManager } from "@core/systems/GameModeManager";

// ❌ FORBIDDEN - Cannot import from Game
import { TerritoryScene } from "@game/scenes/TerritoryScene"; // ❌ NO!
import { LINX_CONFIG } from "@game/config/game.config"; // ❌ NO!

CORE knows about Platform, but NOT about Game.


In GAME Layer

typescript
// ✅ ALLOWED - Everything!
import Phaser from "phaser";
import { baseConfig } from "@platform/base.config";
import { UIManager } from "@core/ui/UIManager";
import { VueUIBridge } from "@core/ui/VueUIBridge";
import { GameModeManager } from "@core/systems/GameModeManager";
import { MyOtherScene } from "@game/scenes/MyOtherScene";

// Game can use Platform, Core, and other Game modules

GAME is the top layer - can use everything below.


🎯 Why This Matters

Reason 1: Core as npm Package

bash
# Extract Core systems
cd src/core
pnpm init
pnpm publish @yourname/cascade-core

# Use in ANY Phaser game:
pnpm install @yourname/cascade-core

import { UIManager, VueUIBridge } from "@yourname/cascade-core";

If Core imports from Game → cannot publish Core separately!


Reason 2: Testing Layers Independently

typescript
// Test Platform config (no Core/Game needed)
describe("Platform Config", () => {
  it("should have correct Phaser settings", () => {
    expect(baseConfig.type).toBe(Phaser.AUTO);
  });
});

// Test Core systems (only Platform needed)
describe("UIManager", () => {
  it("should register components", () => {
    const manager = new UIManager(scene);
    manager.register("test", element);
    expect(manager.isVisible("test")).toBe(false);
  });
});

// Test Game (uses Platform + Core)
describe("TerritoryScene", () => {
  it("should create nodes", () => {
    const scene = new TerritoryScene();
    // ... uses Core systems
  });
});

Each layer tests independently = faster tests, easier debugging.


Reason 3: Multiple Games Share Core

my-games-monorepo/
├── packages/
│   ├── core/           ← Shared Core (UIManager, etc.)
│   ├── platform/       ← Shared Platform (Phaser config)
│   ├── game-rpg/       ← RPG game
│   ├── game-strategy/  ← Strategy game
│   └── game-puzzle/    ← Puzzle game

All 3 games use SAME Core systems.

If Core imports from Game → cannot share!


🔍 How to Check Your Code

Run Dependency Check

bash
# Check for violations
grep -r "@game" src/core/
grep -r "@core" src/platform/

# Should return NOTHING
# If returns results → YOU BROKE ARCHITECTURE!

Visual Studio Code

Install extension: Dependency Cruiser

json
// .dependency-cruiser.js
module.exports = {
  forbidden: [
    {
      name: 'platform-cannot-import-core',
      from: { path: '^src/platform' },
      to: { path: '^src/core' }
    },
    {
      name: 'core-cannot-import-game',
      from: { path: '^src/core' },
      to: { path: '^src/game' }
    },
    {
      name: 'platform-cannot-import-game',
      from: { path: '^src/platform' },
      to: { path: '^src/game' }
    }
  ]
};

📋 CASCADA Inheritance Pattern

Config CASCADA

typescript
// 1. PLATFORM defines base
// platform/base.config.ts
export const baseConfig = {
  type: Phaser.AUTO,
  width: 800,
  height: 600,
  physics: {
    /* ... */
  },
};

// 2. GAME extends platform
// game/config/game.config.ts
import { baseConfig } from "@platform/base.config";

export const gameConfig = {
  ...baseConfig, // ✅ Inherits from Platform
  scene: [BootScene, GameScene],
  // Game-specific additions
};

// ❌ WRONG - Platform extends Game
import { gameConfig } from "@game/config/game.config";
export const baseConfig = { ...gameConfig }; // NO!

System CASCADA

typescript
// 1. CORE defines system
// core/ui/UIManager.ts
export class UIManager {
  register(id: string, element: HTMLElement) {
    // Universal UI management
  }
}

// 2. GAME uses Core system
// game/scenes/MyScene.ts
import { UIManager } from "@core/ui/UIManager";

export class MyScene extends Phaser.Scene {
  private uiManager = new UIManager(this); // ✅ Uses Core
}

// ❌ WRONG - Core uses Game
// core/ui/UIManager.ts
import { MyScene } from "@game/scenes/MyScene"; // NO!

Data Flow CASCADA

User Input (GAME)

UIManager (CORE)

Phaser Scene (PLATFORM)

Phaser Engine (DEPENDENCY)

ONE-WAY FLOW ONLY!

🏗️ File Structure Enforcement

Required Structure

src/
├── platform/        ← Layer 1 (Base)
│   ├── base.config.ts
│   ├── game.config.ts
│   └── types/       ← Platform-specific types

├── core/            ← Layer 2 (Reusable)
│   ├── ui/
│   ├── systems/
│   ├── models/
│   ├── config/
│   ├── utils/
│   └── types/       ← Core-specific types

├── game/            ← Layer 3 (Specific)
│   ├── scenes/
│   ├── ui/
│   ├── config/
│   ├── core/
│   └── types/       ← Game-specific types

└── main.ts          ← Entry point

NEVER put:

  • src/types/ - types belong IN their layers
  • src/utils/ - utilities belong to Core
  • src/components/ - UI belongs to Game
  • src/lib/ - libraries belong to dependencies

🎯 Real Example: Adding New Feature

❌ WRONG WAY - Breaks Architecture

typescript
// You want to add "SaveManager"

// ❌ Put in Game layer
src/game/systems/SaveManager.ts

// ❌ Then use in Core
src/core/ui/UIManager.ts:
import { SaveManager } from "@game/systems/SaveManager"; // NO!

// RESULT: Core depends on Game → cannot extract Core!

✅ RIGHT WAY - Respects Architecture

typescript
// 1. SaveManager is universal → Put in CORE
src/core/systems/SaveManager.ts

// 2. Core can use Core
src/core/ui/UIManager.ts:
import { SaveManager } from "@core/systems/SaveManager"; // ✅ YES!

// 3. Game can use Core
src/game/scenes/MyScene.ts:
import { SaveManager } from "@core/systems/SaveManager"; // ✅ YES!

// RESULT: Clean architecture, Core stays reusable!

📊 Decision Tree: Where Does It Go?

Is it game-specific?

Is this code specific to LINX/your game?

YES → Put in GAME layer
├─ Territory mechanics
├─ LINX-specific UI
├─ Game config
└─ Game scenes

NO → Is it universal for any Phaser game?

    YES → Put in CORE layer
    ├─ UIManager (any game needs UI)
    ├─ EntityRegistry (any game has entities)
    ├─ GameModeManager (multi-scale is Core feature)
    └─ SaveManager (any game can save)

    NO → Is it pure Phaser setup?

        YES → Put in PLATFORM layer
        ├─ Phaser config
        ├─ Physics config
        └─ Renderer settings

🔧 Common Violations and Fixes

Violation 1: Shared Constants

typescript
// ❌ WRONG
// src/constants.ts (root level)
export const GAME_WIDTH = 800;

// PROBLEM: Where does this belong?
// Platform? Core? Game?

FIX:

typescript
// ✅ RIGHT
// platform/base.config.ts
export const PLATFORM_CONSTANTS = {
  DEFAULT_WIDTH: 800,
  DEFAULT_HEIGHT: 600,
};

// core/config/constants.ts
export const CORE_CONSTANTS = {
  UI_FADE_DURATION: 300,
  MODE_TRANSITION: 500,
};

// game/config/game.config.ts
export const GAME_CONSTANTS = {
  TERRITORY_SIZE: 50,
  MAX_NODES: 100,
};

Each layer has its own constants.


Violation 2: Type Sharing

typescript
// ❌ WRONG
// src/types/index.ts (root level)
export interface Player {
  /* ... */
}

// core/systems/EntityRegistry.ts
import { Player } from "@/types"; // ❌ Where is @/types?

FIX:

typescript
// ✅ RIGHT
// core/types/index.ts
export interface Entity {
  /* Core entity */
}

// game/types/index.ts
export interface Player extends Entity {
  /* Game player */
}

// core/systems/EntityRegistry.ts
import { Entity } from "@core/types"; // ✅ Core uses Core types

// game/scenes/GameScene.ts
import { Player } from "@game/types"; // ✅ Game uses Game types
import { Entity } from "@core/types"; // ✅ Game can use Core types

Violation 3: Utility Functions

typescript
// ❌ WRONG
// src/utils/math.ts (root level)
export function clamp(value: number, min: number, max: number) {
  /* ... */
}

FIX:

typescript
// ✅ RIGHT - Utility is universal → belongs to CORE
// core/utils/math.ts
export function clamp(value: number, min: number, max: number) {
  return Math.max(min, Math.min(max, value));
}

// Any layer can import from Core utils
import { clamp } from "@core/utils/math";

🎓 Learning the Pattern

Think Like This:

Question: Where do I put "PlayerInventory"?

Ask yourself:
1. Is it specific to my game?
   YES → GAME layer ✅

   Example: LINX has territories, not inventory
   Another game has inventory
   → Inventory is game-specific → GAME layer

2. Could ANY game use it?
   NO → GAME layer ✅

   If inventory is specific to your RPG mechanics,
   not every game needs inventory → GAME layer
Question: Where do I put "SaveManager"?

Ask yourself:
1. Is it specific to my game?
   NO → Not GAME layer

2. Could ANY game use it?
   YES → CORE layer ✅

   Every game can save data
   → SaveManager is universal → CORE layer
Question: Where do I put "Phaser scale config"?

Ask yourself:
1. Is it specific to my game?
   NO → Not GAME layer

2. Could ANY game use it?
   YES, but it's Phaser setup, not game logic
   → PLATFORM layer ✅

   Phaser configuration = PLATFORM responsibility

📜 Architecture Contract

PLATFORM Promise

"I will provide Phaser configuration. Nothing more."

typescript
// platform/base.config.ts
export const baseConfig: Phaser.Types.Core.GameConfig = {
  type: Phaser.AUTO,
  width: 800,
  height: 600,
  parent: "game-container",
  backgroundColor: "#000000",
  scale: {
    /* ... */
  },
  physics: {
    /* ... */
  },
};

// NO game logic
// NO UI components
// NO game-specific settings
// JUST Phaser setup

CORE Promise

"I will provide universal systems that work in ANY Phaser game."

typescript
// core/ui/UIManager.ts
// Works in ANY game - RPG, Strategy, Puzzle, FPS
export class UIManager {
  // Universal UI management
  // No assumptions about game type
  // No LINX-specific logic
  // No territory-specific logic
}

Test: Can I use UIManager in a completely different game?

  • If YES → ✅ Correct
  • If NO → ❌ Belongs in GAME layer

GAME Promise

"I will implement game-specific logic using Core and Platform."

typescript
// game/scenes/TerritoryGameScene.ts
import { UIManager } from "@core/ui/UIManager"; // ✅ Uses Core
import { baseConfig } from "@platform/base.config"; // ✅ Uses Platform

export class TerritoryGameScene extends Phaser.Scene {
  // LINX-specific territory logic
  // Uses Core systems (UIManager, GameModeManager)
  // Extends Platform config
}

🚨 Critical Don'ts

❌ DON'T: Cross-Layer Imports

typescript
// ❌ Platform imports Core
// platform/base.config.ts
import { UIManager } from "@core/ui/UIManager"; // BREAKS EVERYTHING!

// ❌ Core imports Game
// core/ui/UIManager.ts
import { TerritoryScene } from "@game/scenes/TerritoryScene"; // BREAKS EVERYTHING!

// ❌ Platform imports Game
// platform/base.config.ts
import { GAME_CONFIG } from "@game/config"; // BREAKS EVERYTHING!

These violations make Core non-reusable.


❌ DON'T: Put Files in Wrong Layer

typescript
// ❌ Game-specific code in Core
// core/systems/TerritoryManager.ts ← Territory is LINX-specific!

// ✅ RIGHT
// game/territory/TerritoryManager.ts ← Game-specific belongs in Game

// ❌ Reusable code in Game
// game/utils/SaveManager.ts ← SaveManager is universal!

// ✅ RIGHT
// core/systems/SaveManager.ts ← Universal belongs in Core

❌ DON'T: Create Root-Level Directories

src/
├── utils/        ← ❌ NO! Which layer?
├── types/        ← ❌ NO! Which layer?
├── components/   ← ❌ NO! Which layer?
├── lib/          ← ❌ NO! Which layer?
└── helpers/      ← ❌ NO! Which layer?

Every file MUST belong to a layer.

src/
├── platform/types/   ← ✅ YES! Platform types
├── core/utils/       ← ✅ YES! Core utilities
├── game/components/  ← ✅ YES! Game components

✅ Checklist Before Committing

bash
# Run architecture validator
pnpm check:architecture
# ✅ Passed → safe to commit
# ❌ Failed → shows exact violations with file:line

# Full lint (includes architecture + TypeScript)
pnpm lint

Pre-commit hook runs automatically:

  • Git commit → hook runs pnpm check:architecture
  • If violations → commit BLOCKED
  • If clean → commit proceeds

See Automated Checks guide →


Manual Checks (Alternative)

If you prefer manual validation:

bash
# 1. No Platform → Core imports
grep -r "@core" src/platform/
# Should return: (nothing)

# 2. No Platform → Game imports
grep -r "@game" src/platform/
# Should return: (nothing)

# 3. No Core → Game imports
grep -r "@game" src/core/
# Should return: (nothing)

# 4. No root-level code
ls src/*.ts | grep -v "main.ts"
# Should return: (nothing except main.ts)

If any check fails → FIX BEFORE COMMITTING!


ESLint Integration

ESLint config (eslint.config.js) enforces rules:

javascript
// Configured zones:
{
  target: './src/platform',
  from: './src/core',  // ❌ Blocked
}
{
  target: './src/core',
  from: './src/game',  // ❌ Blocked
}

IDE integration:

  • VS Code shows red squiggles on violations
  • Real-time feedback while coding
  • Fix before running/committing

🎯 Benefits of Strict Architecture

For Developers

Clear mental model - always know where code belongs
Easy testing - test layers independently
Fast debugging - know which layer has the bug
Safe refactoring - changes in Game don't break Core

For Projects

Reusable Core - extract as pnpm package
Multiple games - share Core between games
Team scalability - different teams work on different layers
Version control - layers can have independent versions

For Users

Smaller bundles - only include needed layers
Better performance - optimized layer loading
Stable Core - Core updates don't break your game
Community packages - use Core from others


📖 Further Reading


🚨 Summary: The Golden Rule

╔══════════════════════════════════════════════════════════╗
║                                                          ║
║  PLATFORM → knows nothing                                ║
║                                                          ║
║  CORE → knows Platform, not Game                         ║
║                                                          ║
║  GAME → knows everything (Platform + Core)               ║
║                                                          ║
║  ONE-WAY DEPENDENCY FLOW: GAME → CORE → PLATFORM         ║
║                                                          ║
║  NEVER GO BACKWARDS!                                     ║
║                                                          ║
╚══════════════════════════════════════════════════════════╝

Respect the layers. Respect the architecture. Build great games.


READ THIS BEFORE EVERY NEW FEATURE ⚠️

Back to ArchitectureProject Structure

MIT Licensed