Сьогодні ми розглянемо інтеграцію реляційної й нереляційної баз даних в одному застосунку. Так, це як з'їсти одночасно піцу й суші — звучить дивно, але іноді без такого поєднання не обійтись.
Уявіть собі велике застосунок електронної комерції. У вас є величезний каталог продуктів, інформація про клієнтів, замовлення й платежі. Що зберігати в PostgreSQL? Логічно зберегти там транзакції, які потребують суворої цілісності. А MongoDB чудово підходить для зберігання відгуків користувачів про товари — вони різноманітні за структурою й часто змінюються. У таких випадках комбінування реляційних і нереляційних баз даних дозволяє досягти одночасно надійності й гнучкості.
Архітектура застосунку з двома базами даних
Коли ми говоримо про проєкт з інтеграцією PostgreSQL і MongoDB, нам треба врахувати такий план:
- PostgreSQL для зберігання структурованих і критично важливих даних.
- MongoDB для зберігання великих обсягів неструктурованих даних.
- API-інтерфейс для взаємодії з обома базами даних.
- Чіткий розподіл задач між базами даних.
Головні принципи такої архітектури:
- Розподіл відповідальності: кожен тип даних обробляється в системі, найбільш підходящій для нього.
- Синхронізація даних і їхня цілісність: визначте, де дані мають бути пов'язані й як забезпечити синхронність.
- Продуктивність: забезпечте швидкий доступ до даних там, де це важливо.
Встановлення і налаштування застосунку
Якщо в вас ще не встановлені PostgreSQL і MongoDB, встановіть їх. Для PostgreSQL можна використовувати офіційну документацію PostgreSQL, а для MongoDB — керівництво з інсталяції MongoDB.
Ми також будемо використовувати asyncpg і motor для роботи з PostgreSQL і MongoDB через Python.
Встановимо необхідні залежності:
pip install asyncpg motor fastapi uvicorn
У файлі config.py визначимо параметри підключень для баз даних:
POSTGRES_URL = "postgresql://user:password@localhost:5432/mydb"
MONGO_URL = "mongodb://localhost:27017"
Створимо модуль для підключення до PostgreSQL:
import asyncpg
class PostgresConnection:
def __init__(self, dsn):
self.dsn = dsn
self.connection = None
async def connect(self):
self.connection = await asyncpg.connect(self.dsn)
async def disconnect(self):
if self.connection:
await self.connection.close()
postgres = PostgresConnection(dsn="postgresql://user:password@localhost:5432/mydb")
Створюємо модуль для підключення до MongoDB:
from motor.motor_asyncio import AsyncIOMotorClient
class MongoDBConnection:
def __init__(self, url):
self.url = url
self.client = None
async def connect(self):
self.client = AsyncIOMotorClient(self.url)
self.database = self.client.mydb
async def disconnect(self):
if self.client:
self.client.close()
mongo = MongoDBConnection(url="mongodb://localhost:27017")
Робота з базами даних: приклади
Давайте уявимо, що наш застосунок зберігає дані про замовлення в PostgreSQL і відгуки на товари в MongoDB.
Створимо таблицю замовлень в PostgreSQL:
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_name TEXT NOT NULL,
amount NUMERIC NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Додамо функцію для додавання замовлення:
async def create_order(customer_name: str, amount: float):
query = "INSERT INTO orders (customer_name, amount) VALUES ($1, $2) RETURNING id;"
return await postgres.connection.fetchval(query, customer_name, amount)
Створимо функцію для читання замовлень:
async def get_orders():
query = "SELECT * FROM orders;"
return await postgres.connection.fetch(query)
Відгуки — це неструктуровані дані, тому MongoDB підходить відмінно. Наприклад:
{
"product_id": "12345",
"review": "Чудовий товар!",
"rating": 5,
"timestamp": "2023-10-03T12:00:00Z"
}
Додамо функцію для додавання відгука:
async def add_review(review: dict):
return await mongo.database.reviews.insert_one(review)
Додамо функцію для отримання відгуків про продукт:
async def get_reviews(product_id: str):
return await mongo.database.reviews.find({"product_id": product_id}).to_list(100)
Інтеграція в FastAPI
Тепер об'єднаємо наші бази даних в API. У файлі main.py:
from fastapi import FastAPI
from config import POSTGRES_URL, MONGO_URL
from databases import postgres, mongo, create_order, get_orders, add_review, get_reviews
app = FastAPI()
@app.on_event("startup")
async def startup():
await postgres.connect()
await mongo.connect()
@app.on_event("shutdown")
async def shutdown():
await postgres.disconnect()
await mongo.disconnect()
@app.post("/orders/")
async def create_new_order(customer_name: str, amount: float):
order_id = await create_order(customer_name, amount)
return {"id": order_id}
@app.get("/orders/")
async def list_orders():
orders = await get_orders()
return {"orders": orders}
@app.post("/reviews/")
async def create_review(review: dict):
result = await add_review(review)
return {"id": str(result.inserted_id)}
@app.get("/reviews/{product_id}")
async def list_reviews(product_id: str):
reviews = await get_reviews(product_id)
return {"reviews": reviews}
Запустимо сервер:
uvicorn main:app --reload
Приклад роботи
- Створюємо нове замовлення:
POST /orders/
Відповідь:{ "customer_name": "Іван Іванов", "amount": 1500.75 }{ "id": 1 } - Додаємо відгук:
POST /reviews/
Відповідь:{ "product_id": "12345", "review": "Чудовий товар!", "rating": 5 }{ "id": "64f765ad7b8e5a192cfa3d73" } - Отримуємо список замовлень:
GET /orders/Відповідь:{ "orders": [ { "id": 1, "customer_name": "Іван Іванов", "amount": 1500.75, "created_at": "2023-10-03T14:00:00Z" } ] } - Отримуємо відгуки про продукт:
GET /reviews/12345Відповідь:{ "reviews": [ { "product_id": "12345", "review": "Чудовий товар!", "rating": 5, "timestamp": "2023-10-03T14:30:00Z" } ] }
Особливості та складнощі
Робота з двома базами даних може здатися складною з точки зору їхньої синхронізації. Наприклад, метаінформація з PostgreSQL може знадобитися в MongoDB. У такому випадку краще використовувати фреймворки на кшталт Kafka для забезпечення подійної синхронізації, але це вже тема для іншої лекції.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ