# Forge Services This crate implements the service layer for the Forge application, containing the core business logic and orchestration. ## Architecture The service layer sits between the API/Extension layer and the Repository layer: ```mermaid graph TD Extension[Extension Layer] Services[Services Layer
#40;This Module#41;] Repositories[Repositories Layer] Database[Database] Extension --> Services Services --> Repositories Repositories --> Database ``` ## Responsibilities - **Business Logic:** Enforces game rules and constraints. - **Validation:** Validates input data before processing. - **Orchestration:** Coordinates operations across multiple repositories. - **Error Handling:** Converts technical errors into business-friendly messages. - **Data Transformation:** Handles JSON parsing and model conversion. ## Operational State Policy Most hot-state services in Forge back durable player or organization records and are expected to flush through the save path. `CAD` and `Task` are the current exceptions: they are extension-backed operational state services that are intentionally transient and restart clean with the active server or mission lifecycle. ## Actor Service The `ActorService` manages player lifecycle and state. ### Key Features - **Get-or-Create:** Automatically creates new actors with default values if they don't exist. - **JSON Integration:** Directly accepts JSON strings for updates and creation. - **Partial Updates:** Supports updating specific fields without overwriting the entire actor. - **UID Protection:** Prevents modification of immutable fields like UIDs. ### Usage Example ```rust use forge_services::ActorService; use forge_repositories::RedisActorRepository; // Initialize let repo = RedisActorRepository::new(client); let service = ActorService::new(repo); // 1. Get Actor (creates default if missing) let actor = service.get_actor("76561198123456789".to_string())?; // 2. Create/Overwrite Actor let json_data = r#"{"name": "NewPlayer", "bank": 1000.0}"#; service.create_actor("76561198123456789".to_string(), json_data.to_string())?; // 3. Update Actor (Partial) let update_json = r#"{"bank": 1500.0}"#; service.update_actor("76561198123456789".to_string(), update_json.to_string())?; // 4. Check Existence if service.actor_exists("76561198123456789".to_string())? { println!("Actor exists"); } // 5. Delete Actor // 5. Delete Actor service.delete_actor("76561198123456789".to_string())?; ``` ## Organization Service The `OrgService` manages organization (guild/clan) lifecycle and member management. ### Key Features - **Get-or-Create:** Automatically creates new organizations with default values if they don't exist. - **Member Management:** Handles adding and removing members with validation. - **Duplicate Prevention:** Ensures unique organization IDs and member UIDs. - **Name Validation:** Enforces non-empty organization names. ### Usage Example ```rust use forge_services::OrgService; use forge_repositories::RedisOrgRepository; // Initialize let repo = RedisOrgRepository::new(client); let service = OrgService::new(repo); // 1. Get Organization (creates default if missing) let org = service.get_org("elite_squad".to_string())?; // 2. Create/Overwrite Organization let json_data = r#"{"name": "Elite Squad", "description": "Best players", "leader": "76561198123456789"}"#; service.create_org("elite_squad".to_string(), json_data.to_string())?; // 3. Add Member let member_json = r#"{"uid": "76561198987654321", "rank": "member"}"#; service.add_member("elite_squad".to_string(), member_json.to_string())?; // 4. Update Organization let update_json = r#"{"description": "New description"}"#; service.update_org("elite_squad".to_string(), update_json.to_string())?; // 5. Check Existence if service.org_exists("elite_squad".to_string())? { println!("Organization exists"); } ``` ## Error Handling The service layer returns `Result` where the error string is a descriptive message suitable for logging or displaying to administrators. It wraps lower-level repository errors with additional context. ## Contributing We welcome contributions to the Forge Service Layer! This guide will help you understand how to add new services and maintain the existing codebase. ### Adding a New Service To add a new service (e.g., `ItemService`), follow these steps: 1. **Create the Module**: Create a new file in `src/` (e.g., `src/item.rs`). 2. **Define the Struct**: Define your service struct with a generic repository. ```rust pub struct ItemService { repository: R, } ``` 3. **Implement `new`**: Provide a constructor that accepts the repository. ```rust impl ItemService { pub fn new(repository: R) -> Self { Self { repository } } } ``` 4. **Implement Business Logic**: Add methods for your business logic (e.g., `create_item`, `transfer_item`). ```rust impl ItemService { pub fn create_item(&self, item_id: String, data: String) -> Result { // Validation logic... if self.repository.exists(&item_id)? { return Err("Item already exists".to_string()); } // ... logic to create item let item = Item::new(item_id); self.repository.create(&item)?; Ok(item) } } ``` 5. **Register the Module**: Add your new module to `src/lib.rs` and export the service struct. ```rust pub mod item; pub use item::ItemService; ``` ### Testing - **Unit Tests**: Write unit tests for your business logic. - **Mocking**: Since services use generic repositories, you can easily mock them for testing without a real database. ```rust // Example Mock struct MockRepo; impl ItemRepository for MockRepo { ... } ``` ### Best Practices - **Validation**: Always validate data at the service boundary. Do not rely on the API layer or database constraints alone. - **Error Messages**: Return user-friendly error messages. Avoid exposing internal database errors directly. - **Immutability**: Respect immutable fields (like UIDs). - **Documentation**: Document public methods with doc comments (`///`) explaining their purpose, arguments, and return values.