Designing Game Code Architecture
After a year of development without architecture, the following happens: GameManager is a 3000-line class that knows about everything. PlayerController attaches directly to UIManager because "it's faster that way". Quest system calls SaveSystem, which calls EventSystem, which calls QuestSystem—a circular dependency impossible to untangle without rewriting half the game. A new team member is afraid to touch code because it's unclear what affects what.
This isn't abstract theory—it's what we see in projects that come for optimization or scaling after 6–12 months of active development without an architectural plan.
Patterns That Work in Game Development
Service Locator vs Dependency Injection. Classic choice in Unity. DI frameworks (Zenject/Extenject, VContainer) provide full IoC Container with constructor injection. This is correct from SOLID perspective, but requires team discipline. Service Locator (through static service registry) is a compromise: easier to learn, doesn't require DI container understanding, but loses testability advantages. For small teams (2–4 programmers) we often choose VContainer as balance between rigor and simplicity.
Event-driven architecture via ScriptableObject Events. Pattern from Ryan Hipple (Unite 2017): ScriptableObject as event channel. Components subscribe to GameEvent assets, not knowing about each other. Player takes damage → calls playerDamagedEvent.Raise(). HUD listens to this event and updates HP bar. VFX manager listens and spawns effect. No direct references. This solves coupling problem and simplifies work in large team—artist can connect their effect to event without code changes.
ECS for high-performance code. Unity DOTS (Entities 1.x) justified where you need to update thousands of objects: particle simulation, RTS with hundreds of units, procedural world. ECS entry requires full architecture rewrite—it's not "add on top". Decision made at project start.
State Machine as basis for character logic. Hierarchical State Machine (HSM) for AI and Player Controller—not Animator State Machine (that's only for animations), but separate code implementation. For simple cases—own implementation via enum and switch. For complex (5+ states with transitions)—library like Stateless or Bolt (Ludiq). Explicit states eliminate "spaghetti" from boolean flags: isAttacking && !isStunned && canJump && !isReloading.
Project Structure and Responsibility Division
We design architecture based on two key principles: modules shouldn't know about each other's existence, and business logic shouldn't depend on Unity-specific code.
The second principle allows testing logic through normal C# unit tests without PlayMode. If damage calculation system in RPG is pure C# class without MonoBehaviour—it can be tested in an hour. If it lives inside PlayerController.Update()—testing requires running scene with character.
Typical layered architecture for Unity project:
- Domain—pure C# classes: data models, business logic (DamageCalculator, QuestLogic, SaveData)
- Application—Use Cases, coordination between domain services
- Infrastructure—Unity-specific code (MonoBehaviour, ScriptableObject, Addressables), network calls, persistence
- Presentation—UI, visual effects, audio
Real case: mobile strategy, team of 6. After 4 months development—40% of time went to bug fixing side effects: changing one system broke another. Conducted architectural refactor in 3 weeks: introduced VContainer for DI, extracted Domain layer from GameManager, replaced direct component references with ScriptableObject Events. Next 2 months—zero regression bugs from refactor.
Architecture Design Process
We start with requirements analysis: game type, team, timelines, future features. Architecture should match scale—for hyper-casual with 3 systems and team of 2, Zenject is overkill.
We create Architecture Decision Record (ADR)—document with justification for each key architectural decision. Why VContainer, not Zenject. Why ScriptableObject Events, not UnityEvent. Why Addressables, not Resources. ADR becomes part of project technical documentation.
We prepare project folder structure, naming conventions, template classes for main patterns. First 2 weeks—pair programming with team for pattern reinforcement.
| Task Scale | Estimated Timeline |
|---|---|
| Consultation + architectural plan (new project) | 3–7 days |
| Architecture refactor of existing project | 3–8 weeks |
| Full architecture design with documentation | 2–4 weeks |
| ECS/DOTS implementation in project | 4–10 weeks |
Cost is calculated individually after project audit or concept review.





