245 lines
7.1 KiB
Markdown
245 lines
7.1 KiB
Markdown
# Forge Library
|
|
|
|
This directory contains the core business logic and data layers for the Forge framework, organized into modular, reusable crates that follow clean architecture principles.
|
|
|
|
## Architecture Overview
|
|
|
|
The library follows a **layered architecture** pattern, ensuring separation of concerns and maintainability:
|
|
|
|
```mermaid
|
|
graph TD
|
|
Extension[Extension Layer<br/>#40;ArmA 3 Interface#41;]
|
|
Services[Services Layer<br/>#40;Business Logic#41;]
|
|
Repositories[Repositories Layer<br/>#40;Data Persistence#41;]
|
|
Models[Models Layer<br/>#40;Data Structures#41;]
|
|
|
|
Extension --> Services
|
|
Services --> Repositories
|
|
Repositories --> Models
|
|
```
|
|
|
|
## Modules
|
|
|
|
### Models (`lib/models`)
|
|
|
|
**Purpose**: Defines strict data structures and validation rules for domain entities.
|
|
|
|
**Responsibilities**:
|
|
- Define entity structures (`Actor`, `Org`)
|
|
- Implement validation logic
|
|
- Handle serialization/deserialization (JSON, Arma)
|
|
- Enforce business rules at the data level
|
|
|
|
**Key Features**:
|
|
- Strong typing with Rust structs
|
|
- Built-in validation on creation and updates
|
|
- Automatic email generation for actors
|
|
- Arma-specific type conversions
|
|
|
|
**Documentation**: [models/README.md](models/README.md)
|
|
|
|
### Repositories (`lib/repositories`)
|
|
|
|
**Purpose**: Manages data persistence and retrieval using Redis.
|
|
|
|
**Responsibilities**:
|
|
- Abstract database operations
|
|
- Implement CRUD operations
|
|
- Handle data serialization to Redis formats
|
|
- Manage Redis keys and data structures
|
|
|
|
**Key Features**:
|
|
- Generic over Redis client implementations
|
|
- Hash-based storage for structured data
|
|
- Set-based storage for collections (e.g., org members)
|
|
- Thread-safe operations (`Send + Sync`)
|
|
|
|
**Documentation**: [repositories/README.md](repositories/README.md)
|
|
|
|
### Services (`lib/services`)
|
|
|
|
**Purpose**: Implements business logic, validation, and orchestration of operations.
|
|
|
|
**Responsibilities**:
|
|
- Coordinate between repositories
|
|
- Enforce business rules
|
|
- Handle complex workflows
|
|
- Provide high-level APIs for the extension layer
|
|
|
|
**Key Features**:
|
|
- Generic over repository implementations
|
|
- Stateless service design
|
|
- Get-or-create patterns for entities
|
|
- Comprehensive error handling
|
|
|
|
**Documentation**: [services/README.md](services/README.md)
|
|
|
|
### Shared (`lib/shared`)
|
|
|
|
**Purpose**: Provides common utilities, traits, and helper functions used across all layers.
|
|
|
|
**Responsibilities**:
|
|
- Define shared traits (`RedisClient`)
|
|
- Provide utility functions
|
|
- Common type definitions
|
|
- Cross-cutting concerns
|
|
|
|
**Key Features**:
|
|
- `RedisClient` trait for repository abstraction
|
|
- JSON/Redis value parsing utilities
|
|
- Arma value conversion helpers
|
|
- Reusable helper functions
|
|
|
|
## How It All Works Together
|
|
|
|
### Example: Creating an Actor
|
|
|
|
Here's how the layers interact when creating a new actor:
|
|
|
|
1. **Extension Layer** receives SQF command:
|
|
```rust
|
|
// arma/server/extension/src/actor.rs
|
|
pub fn create_actor(key: String, data: String) -> String {
|
|
// Parse JSON and call service
|
|
ACTOR_SERVICE.create_actor(uid, json_data)
|
|
}
|
|
```
|
|
|
|
2. **Service Layer** validates and orchestrates:
|
|
```rust
|
|
// lib/services/src/actor.rs
|
|
impl<R: ActorRepository> ActorService<R> {
|
|
pub fn create_actor(&self, uid: String, data: String) -> Result<Actor, String> {
|
|
// Create actor model (validates data)
|
|
let actor = Actor::new(uid, data)?;
|
|
|
|
// Persist via repository
|
|
self.repository.create(&actor)?;
|
|
|
|
Ok(actor)
|
|
}
|
|
}
|
|
```
|
|
|
|
3. **Repository Layer** persists to Redis:
|
|
```rust
|
|
// lib/repositories/src/actor.rs
|
|
impl<C: RedisClient> ActorRepository for RedisActorRepository<C> {
|
|
fn create(&self, actor: &Actor) -> Result<(), String> {
|
|
// Convert actor to Redis hash
|
|
let fields = actor.to_redis_fields();
|
|
|
|
// Store in Redis
|
|
self.client.hash_mset(format!("actor:{}", actor.uid), fields)
|
|
}
|
|
}
|
|
```
|
|
|
|
4. **Model Layer** ensures data integrity:
|
|
```rust
|
|
// lib/models/src/actor.rs
|
|
impl Actor {
|
|
pub fn new(uid: String, data: String) -> Result<Self, String> {
|
|
// Validate all fields
|
|
Self::validate(&uid, &data)?;
|
|
|
|
// Create actor with validated data
|
|
Ok(Actor { uid, /* ... */ })
|
|
}
|
|
}
|
|
```
|
|
|
|
## Contributing
|
|
|
|
We welcome contributions to the Forge library! Follow these guidelines to maintain consistency and quality.
|
|
|
|
### Adding a New Model
|
|
|
|
See [models/README.md - Contributing](models/README.md#contributing)
|
|
|
|
**Summary**:
|
|
1. Define struct with validation rules
|
|
2. Implement `new` and `validate` methods
|
|
3. Add serialization traits (`Serialize`, `Deserialize`)
|
|
4. Implement Arma conversions (`FromArma`, `IntoArma`)
|
|
|
|
### Adding a New Repository
|
|
|
|
See [repositories/README.md - Contributing](repositories/README.md#contributing)
|
|
|
|
**Summary**:
|
|
1. Define repository trait with `Send + Sync`
|
|
2. Implement trait for `RedisXRepository<C: RedisClient>`
|
|
3. Use `forge_shared::RedisClient` for operations
|
|
4. Register module in `lib.rs`
|
|
|
|
### Adding a New Service
|
|
|
|
See [services/README.md - Contributing](services/README.md#contributing)
|
|
|
|
**Summary**:
|
|
1. Create service struct generic over repository
|
|
2. Implement constructor and business logic methods
|
|
3. Delegate data operations to repository
|
|
4. Register module in `lib.rs`
|
|
|
|
### Best Practices
|
|
|
|
#### Separation of Concerns
|
|
- **Models**: Only data structures and validation
|
|
- **Repositories**: Only data persistence logic
|
|
- **Services**: Only business logic and orchestration
|
|
- **Shared**: Only common utilities and traits
|
|
|
|
#### Error Handling
|
|
- Use `Result<T, String>` for all fallible operations
|
|
- Provide descriptive error messages
|
|
- Propagate errors up the stack with `?`
|
|
|
|
#### Testing
|
|
- **Models**: Test validation rules
|
|
- **Repositories**: Test with mock Redis clients
|
|
- **Services**: Test with mock repositories
|
|
- **Integration**: Test full stack in extension layer
|
|
|
|
#### Dependencies
|
|
- Models should have minimal dependencies
|
|
- Repositories depend on models and shared
|
|
- Services depend on repositories and models
|
|
- Avoid circular dependencies
|
|
|
|
#### Thread Safety
|
|
- All repository traits require `Send + Sync`
|
|
- Services are stateless and thread-safe
|
|
- Use appropriate synchronization primitives when needed
|
|
|
|
## Module Dependencies
|
|
|
|
```mermaid
|
|
graph TD
|
|
Shared[Shared<br/>#40;No Dependencies#41;]
|
|
Models[Models<br/>#40;Depends on Shared#41;]
|
|
Repositories[Repositories<br/>#40;Depends on Models, Shared#41;]
|
|
Services[Services<br/>#40;Depends on Repositories, Models#41;]
|
|
Extension[Extension<br/>#40;Depends on Services, Repositories, Models, Shared#41;]
|
|
|
|
Shared --> Services
|
|
Services --> Repositories
|
|
Repositories --> Models
|
|
Models --> Extension
|
|
```
|
|
|
|
## Development Workflow
|
|
|
|
1. **Define Model**: Start with data structure and validation
|
|
2. **Create Repository**: Implement persistence layer
|
|
3. **Build Service**: Add business logic
|
|
4. **Expose in Extension**: Create SQF-callable commands
|
|
5. **Test**: Verify each layer independently and together
|
|
|
|
## Additional Resources
|
|
|
|
- [Extension Documentation](../arma/server/extension/README.md)
|
|
- [Redis Operations](../arma/server/extension/src/redis/README.md)
|
|
- [Adapters](../arma/server/extension/src/adapters/README.md)
|