Ответ
Ключевой принцип работы с данными в микросервисах — «Database per Service» (База данных на сервис). Каждый микросервис владеет своими данными и имеет собственную, изолированную от других базу данных (или схему). Это обеспечивает слабую связанность и независимость сервисов.
Взаимодействие одного сервиса со своей БД
Внутри одного сервиса для взаимодействия с его базой данных применяются стандартные подходы:
-
Прямые SQL-запросы через пакет
database/sql: *Идеально для простых запросов, дает полный контроль.// db *sql.DB row := db.QueryRowContext(ctx, "SELECT name, email FROM users WHERE id=$1", userID) var user User if err := row.Scan(&user.Name, &user.Email); err != nil { // handle error } -
Использование ORM (Object-Relational Mapping), например,
GORMилиent: *Упрощает CRUD-операции и сложные запросы, абстрагируя SQL.// db *gorm.DB var user User result := db.First(&user, userID) if result.Error != nil { // handle error } -
Паттерн Repository: *Создает слой абстракции над хранилищем данных, изолируя бизнес-логику от деталей реализации (SQL, NoSQL и т.д.). Это самый гибкий и тестируемый подход.
type UserRepository interface { GetByID(ctx context.Context, id int) (*User, error) } type PostgresUserRepository struct { db *sql.DB } func (r *PostgresUserRepository) GetByID(ctx context.Context, id int) (*User, error) { // ... реализация с SQL-запросом }
Взаимодействие между сервисами
Поскольку прямой доступ к БД другого сервиса запрещен, для межсервисных операций с данными используют:
- API Composition: Один сервис запрашивает данные у другого через его публичный API (REST, gRPC).
- Saga Pattern: Для распределенных транзакций, затрагивающих несколько сервисов. Если один из шагов саги не удается, выполняются компенсирующие транзакции для отката изменений.
- CQRS (Command Query Responsibility Segregation): Разделение операций чтения и записи. Для сложных запросов, требующих данных из многих сервисов, создается отдельный сервис чтения с собственной оптимизированной базой данных, которая асинхронно наполняется событиями из других сервисов (например, через Kafka).
Ключевые выводы:
- Изоляция: Каждый сервис управляет только своей БД.
- Транзакции: Атомарные транзакции ограничены рамками одного сервиса.
- Кэширование: Используйте Redis или Memcached для снижения нагрузки на БД и ускорения ответов.
- Миграции: Каждый сервис самостоятельно управляет миграциями своей схемы БД.
Ответ 18+ 🔞
Да ты послушай, как эти ваши микросервисы с данными работают! Главный принцип — «База данных на сервис», то есть у каждого сервиса своя, блядь, изолированная база. Никакого «дай-ка я в твою таблицу залезу» — это сразу архитектурный пиздец и связанность намертво.
Как один сервис со своей БД общается
Ну тут всё как у людей, стандартные подходы:
-
Прямые SQL-запросы через
database/sql: *Контроль полный, но писать их — это ж, блядь, рутина. Зато надёжно.// db *sql.DB row := db.QueryRowContext(ctx, "SELECT name, email FROM users WHERE id=$1", userID) var user User if err := row.Scan(&user.Name, &user.Email); err != nil { // handle error } -
ORM-ы, типа GORM: *Ну, для ленивых, чтоб не париться с каждым SELECT. Абстракция, удобство, но иногда так наабстрагируют, что запрос нахуй неоптимальный получается.
// db *gorm.DB var user User result := db.First(&user, userID) if result.Error != nil { // handle error } -
Паттерн Repository: *Вот это, блядь, красота! Отделяешь бизнес-логику от хранилища. Захотел сменить базу — просто новую реализацию написал, а не полсервиса перелопатил. Тестировать — одно удовольствие.
type UserRepository interface { GetByID(ctx context.Context, id int) (*User, error) } type PostgresUserRepository struct { db *sql.DB } func (r *PostgresUserRepository) GetByID(ctx context.Context, id int) (*User, error) { // ... реализация с SQL-запросом }
А как сервисы между собой данные таскают?
А вот тут, ёпта, начинается магия! Прямой доступ к чужой базе — это моветон и путь в ад. Вместо этого:
- API Composition: Просто, как двери. Нужны данные из другого сервиса — постучался к нему по REST или gRPC, попросил. Он тебе и выдаст, если захочет.
- Saga Pattern: Это когда нужно сделать распределённую транзакцию, а её нихуя не существует. Делаешь цепочку шагов, и если где-то пиздец, запускаешь компенсирующие транзакции, чтобы откатить всё, что наворотил. Веселье, блядь, на неделю.
- CQRS: Вообще, ебать, мощная штука. Чтение и запись разделяешь. Для сложных отчётов, которые данные из пяти сервисов собирают, заводишь отдельный сервис-читалку со своей оптимизированной БД. А данные туда событиями из Kafka подвозят. Красота!
Коротко о главном, чтобы не ебать мозг:
- Изоляция — наше всё: Твой сервис — твоя база. Чужая — не твоё дело.
- Транзакции: Атомарные только в рамках одного сервиса. Всё остальное — саги и нервные срывы.
- Кэширование: Без Redis или Memcached на больших нагрузках тебя просто разорвёт, как Тузик грелку.
- Миграции: Каждый сервис сам отвечает за свою схему. Никаких общих скриптов на всю систему — это пизда проекту.