← Назад к вопросам

Как выносились изменения в общей базе данных для некоторых микросервисов

2.0 Middle🔥 231 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ответ

Управление миграциями БД в микросервисной архитектуре

В системах с микросервисами каждый сервис обычно имеет свою базу данных (Database per Service pattern). Однако когда требуется скоординировать изменения схемы БД, появляются специфические вызовы.

Инструменты для миграций

Наиболее распространённые подходы:

1. Liquibase — декларативный подход

<changeSet id="1" author="dev">
    <createTable tableName="orders">
        <column name="id" type="BIGINT" autoIncrement="true">
            <constraints primaryKey="true"/>
        </column>
        <column name="user_id" type="BIGINT">
            <constraints nullable="false"/>
        </column>
        <column name="status" type="VARCHAR(50)" defaultValue="PENDING"/>
    </createTable>
</changeSet>

2. Flyway — SQL-миграции с версионированием

-- V1__Initial_schema.sql
CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

В Java применяется через:

Flyway flyway = Flyway.configure()
    .dataSource(url, user, password)
    .load();
flyway.migrate(); // автоматическая миграция при запуске

Координация между микросервисами

Backward compatibility — ключевой принцип:

  • Если User Service добавляет новое поле phone_number, оно должно быть nullable на период перехода
  • Старые версии сервиса не упадут, новые смогут использовать поле

Версионирование API:

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    @GetMapping("/{id}")
    public UserResponse getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        return new UserResponse(user.getId(), user.getEmail(), user.getPhone());
    }
}

Dual-write pattern — для сложных миграций:

  • Первая фаза: новый код пишет в обе схемы (старую и новую)
  • Вторая фаза: переключаем на чтение из новой схемы
  • Третья фаза: удаляем код для старой схемы

Saga Pattern для распределённых транзакций:

@Service
public class OrderSaga {
    
    public void processOrder(Order order) {
        // Шаг 1: создаём заказ
        orderService.create(order);
        
        try {
            // Шаг 2: резервируем товар
            inventoryService.reserve(order.getItems());
            
            // Шаг 3: обрабатываем платёж
            paymentService.charge(order.getAmount());
        } catch (Exception e) {
            // Компенсирующие транзакции (компенсейшены)
            inventoryService.release(order.getItems());
            orderService.cancel(order.getId());
            throw e;
        }
    }
}

Best Practices

  1. Малые, часто применяемые миграции — лучше 10 маленьких, чем 1 большая
  2. Тестирование миграций — каждая миграция должна быть обратимой и протестирована
  3. Мониторинг — отслеживай время выполнения миграции, особенно на больших таблицах
  4. Rolling deployment — запусти новую версию сервиса (с новой схемой) параллельно старой

Этот подход обеспечивает гибкость и надёжность при управлении распределённой архитектурой.