(refactor) more clean architecture
This commit is contained in:
parent
0a45d6e139
commit
8a03bdee96
8 changed files with 278 additions and 53 deletions
28
src/db.rs
Normal file
28
src/db.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// src/db.rs
|
||||
// Почему это здесь?
|
||||
// - Это всё, связанное с инициализацией и конфигурацией базы данных
|
||||
// - Здесь создаётся connection pool, который переиспользуется во всём приложении
|
||||
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use sqlx::PgPool;
|
||||
use std::env;
|
||||
|
||||
/// Инициализирует PgPool (connection pool для PostgreSQL)
|
||||
///
|
||||
/// Connection pool — это набор переиспользуемых соединений к БД.
|
||||
/// Вместо того, чтобы открывать новое соединение для каждого запроса,
|
||||
/// мы берём готовое соединение из пула.
|
||||
///
|
||||
/// Это **критически важно** для производительности:
|
||||
/// - Открытие соединения — медленная операция
|
||||
/// - Connection pool решает эту проблему
|
||||
pub async fn init_pool() -> PgPool {
|
||||
let database_url = env::var("DATABASE_URL")
|
||||
.expect("DATABASE_URL must be set");
|
||||
|
||||
PgPoolOptions::new()
|
||||
.max_connections(5) // Максимум 5 одновременных соединений
|
||||
.connect(&database_url)
|
||||
.await
|
||||
.expect("Failed to create Postgres connection pool")
|
||||
}
|
||||
68
src/main.rs
68
src/main.rs
|
|
@ -1,64 +1,36 @@
|
|||
// Подключаем модули напрямую без mod.rs!
|
||||
mod banking {
|
||||
pub mod client;
|
||||
pub mod models;
|
||||
pub mod error;
|
||||
}
|
||||
// src/main.rs
|
||||
mod route; // Объявляем модуль route (это либо route.rs, либо route/mod.rs)
|
||||
mod db; // Объявляем модуль для работы с БД
|
||||
|
||||
mod services {
|
||||
pub mod account_service;
|
||||
pub mod consent_service;
|
||||
}
|
||||
|
||||
mod handlers {
|
||||
pub mod health;
|
||||
pub mod accounts;
|
||||
}
|
||||
|
||||
mod config;
|
||||
mod db;
|
||||
mod error;
|
||||
|
||||
use axum::{
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
use sqlx::PgPool;
|
||||
|
||||
/// Общее состояние приложения, передаётся во все handlers
|
||||
/// Это паттерн Axum: State<AppState> используется в extract'ах
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub db_pool: PgPool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// 1. Загружаем переменные окружения из .env или secrets (через sops)
|
||||
dotenvy::dotenv().ok();
|
||||
|
||||
// 2. Инициализируем базу данных
|
||||
let db_pool = db::init_pool().await;
|
||||
|
||||
let banking_clients = banking::client::init_all_banks().await;
|
||||
println!("✅ Database connection pool created successfully.");
|
||||
|
||||
let app_state = AppState {
|
||||
db_pool,
|
||||
banking_clients,
|
||||
};
|
||||
// 3. Создаём общее состояние приложения
|
||||
let app_state = AppState { db_pool };
|
||||
|
||||
let app = Router::new()
|
||||
.route("/api/health", get(handlers::health::health_handler))
|
||||
.route("/api/accounts/aggregated", get(handlers::accounts::get_aggregated_accounts))
|
||||
.with_state(app_state);
|
||||
// 4. Конфигурируем роуты (все роуты централизованы в route::router())
|
||||
let app = route::router(app_state);
|
||||
|
||||
// 5. Запускаем сервер
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
|
||||
println!("🚀 Server listening on {}", addr);
|
||||
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub db_pool: sqlx::PgPool,
|
||||
pub banking_clients: BankingClients,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BankingClients {
|
||||
pub vbank: banking::client::BankClient,
|
||||
pub abank: banking::client::BankClient,
|
||||
pub sbank: banking::client::BankClient,
|
||||
}
|
||||
|
|
|
|||
24
src/route.rs
Normal file
24
src/route.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// src/route.rs
|
||||
// Почему это здесь?
|
||||
// - Это центр управления всеми HTTP маршрутами (роутами)
|
||||
// - Здесь объявляются все подмодули (handlers, будущие сервисы и т.д.)
|
||||
// - Здесь собирается финальный Router для Axum
|
||||
|
||||
pub mod handlers; // Объявляем подмодуль handlers
|
||||
|
||||
use axum::{routing::get, Router};
|
||||
use crate::AppState;
|
||||
|
||||
/// Создаёт и возвращает Router со всеми сконфигурированными роутами
|
||||
/// Функция принимает AppState и передаёт его всем handlers'ам
|
||||
pub fn router(app_state: AppState) -> Router {
|
||||
Router::new()
|
||||
// GET /api/health — health-check эндпоинт
|
||||
.route("/api/health", get(handlers::health_handler))
|
||||
|
||||
// Сюда добавим новые роуты по мере разработки:
|
||||
// .route("/api/accounts", get(handlers::accounts::get_accounts))
|
||||
// .route("/api/payments", post(handlers::payments::create_payment))
|
||||
|
||||
.with_state(app_state)
|
||||
}
|
||||
40
src/route/handlers.rs
Normal file
40
src/route/handlers.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// src/route/handlers.rs
|
||||
// Почему это здесь?
|
||||
// - Это всё, что обрабатывает HTTP запросы
|
||||
// - Каждый handler'а — это async функция, которая обрабатывает запрос и возвращает ответ
|
||||
|
||||
use axum::{
|
||||
extract::State,
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
Json,
|
||||
};
|
||||
use serde_json::json;
|
||||
use crate::AppState;
|
||||
|
||||
/// Health-check handler
|
||||
///
|
||||
/// Что он делает:
|
||||
/// 1. Принимает AppState через extract::State (это параметр, который Axum инжектирует)
|
||||
/// 2. Пытается выполнить простой SELECT 1 в БД (проверка подключения)
|
||||
/// 3. Возвращает 200 OK если всё хорошо, или 500 если БД недоступна
|
||||
pub async fn health_handler(
|
||||
State(state): State<AppState>,
|
||||
) -> impl IntoResponse {
|
||||
// Пытаемся выполнить простой запрос к БД
|
||||
let result = sqlx::query("SELECT 1")
|
||||
.execute(&state.db_pool)
|
||||
.await;
|
||||
|
||||
// Обрабатываем результат
|
||||
match result {
|
||||
Ok(_) => (
|
||||
StatusCode::OK,
|
||||
Json(json!({ "status": "Database connection is successful." })),
|
||||
).into_response(),
|
||||
Err(e) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({ "status": format!("Database connection failed: {}", e) })),
|
||||
).into_response(),
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue