Skip to main content
When you open the SET: 3D Edition project for the first time, you’ll find a carefully structured folder hierarchy that maps one-to-one onto the project’s Clean Architecture layers. This page walks you through every folder, every assembly definition, and every key scene so you can find what you’re looking for in under five minutes.

Why this page exists

Codebase disorientation is one of the most common sources of architecture violations. A developer who doesn’t know where ISetValidator lives might accidentally implement validation logic in a MonoBehaviour. This tour prevents that by giving you a reliable mental map before you write a single line.

Key responsibilities

This page owns three navigation outcomes for every contributor:

Top-level folder structure

Everything production-relevant lives under Assets/_Project/. Third-party libraries, tests, and editor tools each have their own top-level sibling folders to keep them cleanly separated from game code.
Never modify anything inside _External/. Third-party packages belong to their respective package managers. If a package needs patching, raise it with the tech lead.

The entry-point scene

Assets/_Project/Scenes/Bootstrap.unity is the only scene listed in Build Settings. It performs three jobs when it runs:
  1. Instantiates the VContainer LifetimeScope that wires all dependency injection bindings.
  2. Loads persistent manager objects (audio, platform services, local save).
  3. Transitions additively into MainMenu.unity.
Every other scene (GameBoard, PostMatch) is loaded additively from within the game flow — never directly. Always use Bootstrap as your editor Play entry point.

Assembly definition map

The project uses Unity Assembly Definition files (.asmdef) to enforce layer boundaries at compile time. If you reference a type from the wrong assembly, the project will not compile — this is intentional. The dependency graph looks like this: SET.Presentation binds to SET.Infrastructure only at runtime through VContainer’s DI container inside the Bootstrap scene. There is no compile-time reference — Presentation simply knows the interfaces defined in Application, and VContainer supplies the concrete implementations at startup.

What lives in each layer

Domain layer — _Project/Domain/

The innermost layer. Contains everything needed to represent and validate a game of SET, with zero external dependencies. Match is the aggregate root — code outside the Domain layer interacts with game state exclusively through Match’s public API, never by reaching into its child entities directly.

Application layer — _Project/Application/

Orchestration logic. Depends only on SET.Domain. GameSession is the central state machine for a match. It receives IGameCommand objects, coordinates Domain entities, and pushes state changes out as R3 observables. It has no knowledge of Unity, Nakama, or the UI.

Infrastructure layer — _Project/Infrastructure/

Implements the interfaces defined in Application using real external SDKs. All Nakama types (e.g., IMatch, IApiUser) are converted into plain domain DTOs before they cross the Infrastructure boundary into Application or Domain.

Presentation layer — _Project/Presentation/

Everything Unity-dependent. Depends on Domain and Application, never Infrastructure. MonoBehaviours in this layer are thin views. They bind UI elements to ViewModel properties and forward user input. They never contain game logic.

Config files

Runtime configuration lives in Assets/_Project/Data/ as plain JSON files: These files are loaded at runtime via Resources.Load or Addressables; parsing logic lives in Infrastructure/Serialization/.

The golden rule

Dependencies always point inward. Outer layers know about inner layers. Inner layers know nothing about outer layers.
If you’re in the Domain layer and you’re about to add using SET.Application; — stop. That reference goes the wrong direction. If you’re in Presentation and you’re about to add using SET.Infrastructure; — stop. Use the interface from Application instead, and let VContainer supply the implementation.

Implementation checklist

Use this checklist when adding a new file to confirm it belongs in the right place:
  • New domain type? File goes in Assets/_Project/Domain/ (entity, value object, enum, or interface) with no external references
  • New application interface? File goes in Assets/_Project/Application/Services/ — Infrastructure implements it, Presentation consumes it
  • New Infrastructure class? File goes in Assets/_Project/Infrastructure/<Subsystem>/ — implement the Application interface, register in Bootstrap
  • New MonoBehaviour view? File goes in Assets/_Project/Presentation/Views/ with a paired ViewModel in Presentation/ViewModels/
  • New scene? File goes in Assets/_Project/Scenes/ — never inside a layer subfolder
  • New test? EditMode tests for Domain/Application in Assets/_Tests/EditMode/; PlayMode integration tests in Assets/_Tests/PlayMode/
  • New config? JSON config files go in Assets/_Project/Data/; parsed by Infrastructure serialization code

Common mistakes

Common repo navigation mistakes:
  • Adding using UnityEngine; to Domain or Application — these assemblies have no Unity reference and the compiler will reject it. If your type needs Unity, it belongs in Presentation (views, VFX) or Infrastructure (audio, platform services).
  • Directly referencing NakamaMultiplayerService from Presentation — Presentation only knows IMultiplayerService. The concrete class is invisible to it at compile time by design.
  • Placing scene files inside _Project/Presentation/Scenes/ — all .unity files live in _Project/Scenes/, not inside the Presentation subfolder.
  • Editing files inside _External/ — third-party packages should never be modified directly. Changes will be lost on the next package update.
  • Writing tests in PlayMode/ when EditMode/ will do — Domain and Application logic needs no Unity lifecycle. EditMode tests run faster and don’t require entering Play mode.

Prerequisites

Software, accounts, and setup steps before you open the project.

First Tasks

Where to start contributing — Domain-layer work that needs no Unity runtime.

Architecture Layers

Deep dive into what each layer owns, forbids, and how they communicate.

Assembly Definitions

How .asmdef files enforce compile-time dependency rules.