Dojo Framework Overview
What is Dojo?
Dojo is a comprehensive framework for building provable games and autonomous worlds on Starknet. It combines the power of Cairo smart contracts with Entity-Component-System (ECS) architecture to create scalable, composable onchain applications.
Key Benefits:- Provable: All game logic and state changes are verifiable onchain
- Composable: Modular design allows for easy extension and integration
- Scalable: Optimized for high-performance onchain applications
- Developer-friendly: Rich tooling and familiar development patterns
Understanding ECS Architecture
Entity-Component-System (ECS) is a design pattern that separates data from logic, enabling highly modular and scalable applications.
The Problem ECS Solves: Traditional object-oriented programming can lead to complex inheritance hierarchies and tight coupling between data and behavior. ECS addresses these issues by decomposing your application into three distinct parts:
The ECS Trinity
┌─────────────────────────────────────────────────────────────┐
│ ECS PATTERN │
├─────────────────────────────────────────────────────────────┤
│ │
│ ENTITIES COMPONENTS SYSTEMS │
│ (What) (Data) (Logic) │
│ │
│ Player Position: {x, y} Movement │
│ Enemy Health: {hp} Combat │
│ Item Inventory: {items} Crafting │
│ │
└─────────────────────────────────────────────────────────────┘
- Entities: The objects in your game — characters, items, etc.
- Components: The properties of your entities — position, durability, etc.
- Systems: The rules that govern your game — movement, combat, etc.
With ECS, you assign components to entities, and operate on them using systems. This approach allows for a more modular and scalable design, as well as better performance and memory usage. Entities are typically represented as a unique identifier, to which components are assigned. Systems can then operate over large numbers of entities at once, depending on the components they have.
As an example, a combat system might reduce the durability of all weapons used in a battle, while a rest system might increase the health of all members of a party.
ECS Benefits:- Modularity: Components can be mixed and matched across entity types
- Performance: Systems can efficiently process large numbers of entities
- Flexibility: Easy to add new components or systems without affecting existing code
Real-World Example
Consider a simple RPG where both players and monsters can move and fight:
// Components (Data)
#[derive(Copy, Drop, Serde)]
#[dojo::model]
struct Position {
#[key]
entity_id: u32,
x: u32,
y: u32,
}
#[derive(Copy, Drop, Serde)]
#[dojo::model]
struct Health {
#[key]
entity_id: u32,
hp: u32,
max_hp: u32,
}
// Systems (Logic)
#[starknet::interface]
trait IGameSystem<T> {
fn move(ref self: T, entity_id: u32, direction: Direction);
fn fight(ref self: T, attacker_id: u32, target_id: u32);
}
#[dojo::contract]
mod game_system {
use super::{Position, Health, Direction, IGameSystem};
use dojo::model::{ModelStorage};
use dojo::world::{WorldStorage, WorldStorageTrait};
#[abi(embed_v0)]
impl GameSystemImpl of IGameSystem<ContractState> {
fn move(ref self: ContractState, entity_id: u32, direction: Direction) {
let mut world = self.world(@"my_game");
let mut position: Position = world.read_model(entity_id);
// Update position based on direction
world.write_model(@position);
}
fn fight(ref self: ContractState, attacker_id: u32, target_id: u32) {
let mut world = self.world(@"my_game");
let mut attacker_health: Health = world.read_model(attacker_id);
let mut target_health: Health = world.read_model(target_id);
// Handle combat logic
world.write_model(@target_health);
}
}
}
Both player and monsters can have Position and Health components, and the same systems work for both.
Dojo's ECS Implementation
Dojo adapts ECS for the onchain environment with three core concepts:
1. Models (Components)
Models are Cairo structs that define your application's data structures. They act as components in the ECS pattern.
#[derive(Copy, Drop, Serde)]
#[dojo::model] // Defines a model, used to generate Torii events
struct Position {
#[key] // Defines a model's key, like an ORM primary key
pub player: ContractAddress,
// The rest of the model's attributes
pub x: u32,
pub y: u32,
}
- Automatic indexing: All model changes are automatically indexed by Torii
- Type safety: Full Cairo type system support
- Composability: Models can be reused across different entity types
2. Systems (Smart Contracts)
Systems implement your application's business logic as Cairo contract functions.
// First define the interface
#[starknet::interface]
trait IActions<T> {
fn move_player(ref self: T, direction: Direction);
}
#[dojo::contract] // Defines a Dojo contract
mod actions {
use super::IActions;
use starknet::{ContractAddress, get_caller_address};
use dojo::model::{ModelStorage};
use dojo::world::{WorldStorage, WorldStorageTrait};
#[abi(embed_v0)]
impl ActionsImpl of IActions<ContractState> {
fn move_player(ref self: ContractState, direction: Direction) {
// Get the world and player entity key
let mut world = self.world(@"my_game");
let player = get_caller_address();
// Read current position
let mut position: Position = world.read_model(player);
// Update position based on direction
match direction {
Direction::Up => position.y += 1,
Direction::Down => position.y -= 1,
Direction::Left => position.x -= 1,
Direction::Right => position.x += 1,
}
// Write updated position
world.write_model(@position);
}
}
}
- Permissioned access: Fine-grained control over who can modify what
- Atomic operations: All state changes happen in a single transaction
- Event emission: Automatic event generation for state changes
3. World (Resource Registry)
The World contract serves as the central coordinator, managing all models and systems.
// The World provides a unified interface for all operations
let mut world = self.world(@"my_game");
// Cairo's typing tells us we are querying the player's position
let position: Position = world.read_model(player_id);
// Update game state
world.write_model(@new_position);
// Emit custom events if needed
world.emit_event(@PlayerMoved { player, direction });
- Centralized state: Single source of truth for all application data
- Permission management: Hierarchical authorization system
- Upgradeability: Safe model and system upgrades
- Introspection: Rich metadata and schema information
Architecture Overview
Development Workflow
- Design Models: Define your data structures as Dojo models
- Implement Systems: Create contract functions that operate on models
- Configure Permissions: Set up authorization for your systems
- Deploy to World: Register your models and systems
- Build Client: Use indexed data to create rich user experiences
Best Practices
Model Design
- Keep models small and focused - Each model should represent a single concept
- Design for reusability - Models should be composable across different entity types
- Plan for upgrades - Consider future schema changes when designing models
- Always derive required traits - All models must derive
Drop
andSerde
- Use key fields correctly - All
#[key]
fields must come before non-key fields
System Design
- Minimize gas costs - Batch operations and optimize storage access
- Handle errors gracefully - Use assertions and proper error handling
- Design for permissions - Group related systems by permission requirements
- Define interfaces first - Always create a
#[starknet::interface]
before implementing systems - Use proper mutability - Make world reference mutable when writing models
Architecture
- Separate concerns - Keep data (models) and logic (systems) distinct
- Use events effectively - Emit events for important state changes
- Plan your namespaces - Organize resources logically and use consistent namespace strings
- Follow ECS principles - Compose entities using multiple small, focused models
Next Steps
Ready to start building with Dojo? Here's your learning path:
World Contract
Understand the central coordinator that manages all your application's state and permissions.
Models
Learn how to define and manage your application's data structures with Dojo models.
Systems
Implement business logic and state transitions with Dojo systems.
Testing
Master the testing framework to ensure your application works correctly.