title: Navius Architectural Principles description: Core architectural principles and patterns guiding the design of the Navius framework category: reference tags:

  • architecture
  • design
  • principles
  • patterns related:
  • directory-organization.md
  • ../../guides/development/project-navigation.md
  • ../standards/naming-conventions.md last_updated: March 27, 2025 version: 1.0

Navius Architectural Principles

Overview

This reference document outlines the core architectural principles and design patterns that guide the development of the Navius framework. These principles ensure the framework remains maintainable, extensible, and performant as it evolves.

Core Principles

1. Modular Design

Navius is built around a modular architecture that separates concerns and enables components to evolve independently.

Key aspects:

  • Self-contained modules with clearly defined responsibilities
  • Minimal dependencies between modules
  • Ability to replace or upgrade individual components without affecting others
  • Configuration-driven composition of modules

2. Explicit Over Implicit

Navius favors explicit, clear code over "magic" behavior or hidden conventions.

Key aspects:

  • Explicit type declarations and function signatures
  • Clear error handling paths
  • Minimal use of macros except for well-defined, documented purposes
  • No "convention over configuration" that hides important behavior

3. Compile-Time Safety

Navius leverages Rust's type system to catch errors at compile time rather than runtime.

Key aspects:

  • Strong typing for all API interfaces
  • Use of enums for representing states and variants
  • Avoiding dynamic typing except when necessary for interoperability
  • Proper error type design for comprehensive error handling

4. Performance First

Performance is a primary design goal, not an afterthought.

Key aspects:

  • Minimal runtime overhead
  • Efficient memory usage
  • Asynchronous by default
  • Careful consideration of allocations and copying
  • Benchmarking as part of the development process

5. Developer Experience

The framework prioritizes developer experience and productivity.

Key aspects:

  • Intuitive API design
  • Comprehensive documentation
  • Helpful error messages
  • Testing utilities and patterns
  • Minimal boilerplate code

Architectural Patterns

Clean Architecture

Navius follows a modified Clean Architecture pattern with distinct layers:

               ┌─────────────────┐
               │  Controllers    │
               │  (HTTP Layer)   │
               └────────┬────────┘
                        │
                        ▼
               ┌─────────────────┐
               │    Services     │
               │ (Business Logic)│
               └────────┬────────┘
                        │
                        ▼
               ┌─────────────────┐
               │  Repositories   │
               │  (Data Access)  │
               └────────┬────────┘
                        │
                        ▼
               ┌─────────────────┐
               │   Data Store    │
               │ (DB, Cache, etc)│
               └─────────────────┘

Principles applied:

  • Dependencies point inward
  • Inner layers know nothing about outer layers
  • Domain models are independent of persistence models
  • Business logic is isolated from I/O concerns

Dependency Injection

Navius uses a trait-based dependency injection pattern to enable testability and flexibility:

#![allow(unused)]
fn main() {
// Define a service that depends on a repository trait
pub struct UserService<R: UserRepository> {
    repository: R,
}

impl<R: UserRepository> UserService<R> {
    pub fn new(repository: R) -> Self {
        Self { repository }
    }
    
    pub async fn get_user(&self, id: Uuid) -> Result<User, Error> {
        self.repository.find_by_id(id).await
    }
}

// In production code
let db_repository = PostgresUserRepository::new(db_pool);
let service = UserService::new(db_repository);

// In test code
let mock_repository = MockUserRepository::new();
let service = UserService::new(mock_repository);
}

Error Handling

Navius uses a centralized error handling approach:

#![allow(unused)]
fn main() {
// Core error type
pub enum AppError {
    NotFound,
    Unauthorized,
    BadRequest(String),
    Validation(Vec<ValidationError>),
    Internal(anyhow::Error),
}

// Converting from domain errors
impl From<DatabaseError> for AppError {
    fn from(error: DatabaseError) -> Self {
        match error {
            DatabaseError::NotFound => AppError::NotFound,
            DatabaseError::ConnectionFailed(e) => AppError::Internal(e.into()),
            // Other conversions...
        }
    }
}

// Converting to HTTP responses
impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, error_message) = match self {
            AppError::NotFound => (StatusCode::NOT_FOUND, "Resource not found"),
            AppError::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized"),
            // Other mappings...
        };
        
        // Create response
        (status, Json(ErrorResponse { message: error_message.to_string() })).into_response()
    }
}
}

Middleware Pipeline

Navius uses a middleware-based pipeline for processing HTTP requests:

#![allow(unused)]
fn main() {
let app = Router::new()
    .route("/api/users", get(list_users).post(create_user))
    .layer(TracingLayer::new_for_http())
    .layer(CorsLayer::permissive())
    .layer(AuthenticationLayer::new())
    .layer(CompressionLayer::new())
    .layer(TimeoutLayer::new(Duration::from_secs(30)));
}

Configuration Management

Navius uses a layered configuration system:

  1. Default values
  2. Configuration files
  3. Environment variables
  4. Command-line arguments

This ensures flexibility while maintaining sensible defaults:

#![allow(unused)]
fn main() {
// Configuration loading order
let config = ConfigBuilder::new()
    .add_defaults()
    .add_file("config/default.toml")
    .add_file(format!("config/{}.toml", environment))
    .add_environment_variables()
    .add_command_line_args()
    .build()?;
}

API Design Principles

Resource-Oriented

APIs are structured around resources and their representations.

Consistent Error Handling

A standardized error response format is used across all API endpoints.

Proper HTTP Method Usage

HTTP methods match their semantic meaning (GET, POST, PUT, DELETE, etc.).

Versioning Support

APIs support versioning to maintain backward compatibility.

Database Access Principles

Repository Pattern

Data access is encapsulated behind repository interfaces.

Transaction Management

Explicit transaction boundaries with proper error handling.

Migration-Based Schema Evolution

Database schemas evolve through explicit migrations.

Testing Principles

Test Pyramid

Balance between unit, integration, and end-to-end tests.

Test Isolation

Tests should not depend on each other or external state.

Mocking External Dependencies

External dependencies are mocked for deterministic testing.