Skip to content

Project Structure - Where to Put What

Clear rules for maintaining clean architecture


Golden Rule

Ask yourself: Is this code universal for any game, or specific to my game?

Universal → src/core/
Game-specific → src/game/

📂 Directory Structure

src/
├── platform/          ← Phaser base configuration (engine level)
├── core/              ← Universal reusable systems (engine level)
├── game/              ← Your specific game logic
└── landing/           ← Presentation landing page

Layer Rules

src/platform/ - Phaser Foundation

What goes here:

  • Phaser base configuration (base-config.ts)
  • Canvas settings
  • Physics engine config
  • Scale manager settings
  • FPS limits

What does NOT go here:

  • Game scenes
  • Core systems
  • Game logic
  • UI components

Dependencies:

  • Phaser only
  • NO imports from @core/ or @game/

Example:

typescript
//  CORRECT - src/platform/base-config.ts
import Phaser from "phaser";

export const baseConfig: Phaser.Types.Core.GameConfig = {
  type: Phaser.AUTO,
  scale: {
    mode: Phaser.Scale.RESIZE,
    // ...
  },
  physics: {
    default: "arcade",
  },
  scene: [], // NO scenes here!
};

src/core/ - Universal Engine Systems

What goes here:

  • UIManager (framework-agnostic)
  • Framework bridges (VueUIBridge, ReactUIBridge, etc.)
  • ThemeManager (class + interface, NO concrete themes)
  • Sphere of Influence (IoS)
  • EntityRegistry
  • GameModeManager
  • ViewportManager
  • Generic models (Region, Action, GameMode)
  • Universal utilities

What does NOT go here:

  • Game-specific logic
  • Concrete themes (those go to game/)
  • Game scenes
  • Game UI components (.vue files)
  • Game constants

Dependencies:

  • Phaser
  • Platform (@platform/)
  • External libraries (Vue, React, etc.)
  • NO imports from @game/

Test:

bash
# Core must NOT import from game
grep -r "@game" src/core/ --exclude="*.md"
# Expected: 0 matches

Examples:

typescript
//  CORRECT - src/core/ui/ui-manager.ts
import Phaser from "phaser";

export class UIManager {
  // Framework-agnostic, can work with ANY game
}
typescript
//  CORRECT - src/core/config/theme-manager.ts
export interface VisualTheme {
  id: string;
  colors: { [key: string]: number };
  // Generic interface, no concrete themes
}

export class ThemeManager<T extends VisualTheme> {
  // Universal class, games define their themes
}
typescript
//  WRONG - src/core/config/my-game-themes.ts
export const THEME_NEON = { ... }; // This is game-specific!
// Should be in src/game/config/

src/game/ - Your Game Logic

What goes here:

  • Game configuration (config/game-config.ts extends baseConfig)
  • Game scenes (scenes/)
  • Game entities, mechanics, systems
  • Game UI components (.vue, .tsx, etc.)
  • Concrete themes (config/my-themes.ts)
  • Game-specific constants
  • Assets, sprites specific to your game

Dependencies:

  • Core (@core/)
  • Platform (@platform/)
  • Phaser
  • External libraries

Structure example:

src/game/
├── config/
│   ├── game-config.ts       ← Extends baseConfig, defines scenes
│   └── linx-themes.ts        ← Concrete themes for LINX game
├── scenes/
│   ├── boot-scene.ts
│   ├── preloader-scene.ts
│   └── main-menu-scene.ts
├── territory/               ← LINX game logic
│   └── scenes/
│       └── territory-game-scene.ts
└── ui/                      ← LINX UI components
    ├── main-menu-ui.vue
    └── game-hud.vue

Examples:

typescript
//  CORRECT - src/game/config/game-config.ts
import { baseConfig } from "@platform/base-config";
import { BootScene } from "@game/scenes/boot-scene";

export const gameConfig = {
  ...baseConfig,  // Extend platform config
  scene: [BootScene, PreloaderScene, ...],
};
typescript
//  CORRECT - src/game/config/linx-themes.ts
import { ThemeManager, VisualTheme } from "@core/config/theme-manager";

export const THEME_NEON: VisualTheme = {
  id: "neon",
  colors: { /* LINX-specific colors */ },
};

export const themeManager = new ThemeManager([THEME_NEON, ...]);
typescript
//  CORRECT - src/game/scenes/my-game-scene.ts
import { UIManager } from "@core/ui/ui-manager";
import { VueUIBridge } from "@core/ui/vue-ui-bridge";
import MyUI from "@game/ui/my-ui.vue";

export class MyGameScene extends Phaser.Scene {
  create() {
    const uiManager = new UIManager(this);
    const bridge = new VueUIBridge(uiManager);
    bridge.mount("hud", MyUI);
  }
}

🤔 Decision Tree

"Where should I put this file?"

Is it Phaser base configuration?
├─ YES → src/platform/
└─ NO
   └─ Can ANY game use this?
      ├─ YES → src/core/
      └─ NO → src/game/

Examples:

Q: I want to add a new UI framework bridge (SvelteUIBridge).
A: src/core/ui/svelte-ui-bridge.ts
Why: Any game can use Svelte.

Q: I want to define my game's visual themes.
A: src/game/config/my-themes.ts
Why: Themes are specific to your game.

Q: I want to add a new combat system.
A: src/game/systems/combat-system.ts
Why: Combat is game-specific logic.

Q: I want to improve Sphere of Influence.
A: src/core/systems/sphere-of-influence-system.ts
Why: IoS is universal, any game can use it.

Q: I want to add a new game mode (ULTRA).
A: Modify src/core/models/game-mode.ts
Why: Game modes are part of the engine's Multi-Scale system.

Q: I want to change Phaser's FPS limit.
A: src/platform/base-config.ts
Why: FPS is Phaser foundation setting.

Q: I created a cool Vue component for my menu.
A: src/game/ui/my-menu.vue
Why: Your menu is game-specific.

Q: I created a generic modal dialog Vue component.
A: Tricky! If it's truly generic → src/core/ui/components/modal.vue
Why: Reusable across games. But if it has game-specific styling → src/game/ui/


Benefits of Following Structure

1. Extract engine as pnpm package

bash
# Core + Platform → pnpm package
pnpm publish @yourcompany/cascade-engine

# Use in new game
pnpm install @yourcompany/cascade-engine

If you put game logic in core/, this becomes impossible!

2. Parallel team development

Team A: Platform improvements
Team B: Core new systems
Team C: Game #1 logic
Team D: Game #2 logic

No conflicts!

3. Testable layers

bash
pnpm test src/platform/  # Test Phaser config
pnpm test src/core/      # Test engine systems
pnpm test src/game/      # Test game logic

4. Clean dependencies

Game → uses Core + Platform
Core → uses Platform
Platform → uses nothing (except Phaser)

Linear dependency graph!

🚨 Common Mistakes

Mistake 1: Game logic in Core

typescript
//  WRONG - src/core/systems/linx-territory-system.ts
export class LinxTerritorySystem {
  // This is LINX-specific, not universal!
}

Fix: Move to src/game/territory/systems/territory-system.ts


Mistake 2: Concrete themes in Core

typescript
//  WRONG - src/core/config/my-themes.ts
export const THEME_NEON = { ... };
export const THEME_MINIMAL = { ... };

Fix: Move to src/game/config/my-themes.ts

Core should have:

typescript
//  CORRECT - src/core/config/theme-manager.ts
export interface VisualTheme { ... }
export class ThemeManager { ... }

Mistake 3: Scenes in Platform

typescript
//  WRONG - src/platform/base-config.ts
import { MyScene } from "@game/scenes/my-scene";

export const baseConfig = {
  scene: [MyScene], // NO!
};

Fix:

typescript
//  Platform - src/platform/base-config.ts
export const baseConfig = {
  scene: [], // Empty, games define scenes
};

//  Game - src/game/config/game-config.ts
import { baseConfig } from "@platform/base-config";
import { MyScene } from "@game/scenes/my-scene";

export const gameConfig = {
  ...baseConfig,
  scene: [MyScene], // Scenes defined by game
};

Mistake 4: Core importing Game

typescript
//  WRONG - src/core/ui/vue-ui-bridge.ts
import MyGameComponent from "@game/ui/my-component.vue";

Fix: Core should never know about game specifics!


📋 Checklist Before Adding File

  • [ ] Is this universal for any game? → src/core/
  • [ ] Is this Phaser base config? → src/platform/
  • [ ] Is this specific to my game? → src/game/
  • [ ] Did I check import rules?
    • [ ] Platform doesn't import @core/@game
    • [ ] Core doesn't import @game
    • [ ] Game can import @core/@platform
  • [ ] Can I extract engine without this file breaking?

Validation

Run these commands to check compliance:

bash
# Platform must NOT import @core/@game
grep -r "@core\|@game" src/platform/
# Expected: 0 matches 

# Core must NOT import @game
grep -r "@game" src/core/ --exclude="*.md"
# Expected: 0 matches 

# Game CAN import @core/@platform
grep -r "@core\|@platform" src/game/
# Expected: many matches

Follow the structure. Get the benefits.

MIT Licensed