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 pageLayer 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:
// 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:
# Core must NOT import from game
grep -r "@game" src/core/ --exclude="*.md"
# Expected: 0 matchesExamples:
// CORRECT - src/core/ui/ui-manager.ts
import Phaser from "phaser";
export class UIManager {
// Framework-agnostic, can work with ANY game
}// 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
}// 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.tsextendsbaseConfig) - 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.vueExamples:
// 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, ...],
};// 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, ...]);// 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
# Core + Platform → pnpm package
pnpm publish @yourcompany/cascade-engine
# Use in new game
pnpm install @yourcompany/cascade-engineIf 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
pnpm test src/platform/ # Test Phaser config
pnpm test src/core/ # Test engine systems
pnpm test src/game/ # Test game logic4. 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
// 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
// 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:
// CORRECT - src/core/config/theme-manager.ts
export interface VisualTheme { ... }
export class ThemeManager { ... }Mistake 3: Scenes in Platform
// WRONG - src/platform/base-config.ts
import { MyScene } from "@game/scenes/my-scene";
export const baseConfig = {
scene: [MyScene], // NO!
};Fix:
// 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
// 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
- [ ] Platform doesn't import
- [ ] Can I extract engine without this file breaking?
Validation
Run these commands to check compliance:
# 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 matchesFollow the structure. Get the benefits.