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

Когда бы использовал метод интеграции через базу данных?

1.7 Middle🔥 151 комментариев
#Основы Java

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

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

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

Интеграция через базу данных: когда и почему

Интеграция через БД — паттерн, когда несколько приложений или сервисов обмениваются данными через общую базу данных, а не через API. Это спорный подход, но есть ситуации, где он оправдан.

Когда использовать БД интеграцию

1. Легаси системы без API

Если два системы работают в одной компании десятки лет и оба писали разные команды — переделать их на микросервисы слишком дорого:

// Старая система (1990s code)
public class LegacyOrderProcessor {
    public void processOrder() {
        // SELECT * FROM legacy_orders WHERE processed = false
        // INSERT INTO shared_db.processed_orders
    }
}

// Новая система (Java 2020+)
public class ModernBillingService {
    public void syncOrders() {
        // SELECT * FROM shared_db.processed_orders
        // UPDATE billing_records
    }
}

Плюсы:

  • Не нужно переписывать legacy сервис
  • Гарантированно консистентно (одна БД)

Минусы:

  • Tight coupling между системами
  • Сложнее масштабировать
  • Shared db может стать bottleneck

2. Высокочастотная синхронизация данных

Если нужна абсолютная консистентность в real-time (не eventual consistency), и асинхронная интеграция не подходит:

// Сценарий: финансовый транспорт между счётами
// Нельзя потерять ни один рубль

@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferMoney(BigDecimal amount, Account from, Account to) {
    // Обе системы видят изменение сразу
    accountRepository.debit(from.getId(), amount);
    // ... некий код второй системы ...
    auditRepository.save(new Transaction(from, to, amount));
    // COMMIT — обе системы согласованы
}

Когда это работает:

  • Оба приложения контролируются одной командой
  • Масштаб небольшой (< 10 request/sec)
  • Требуется ACID гарантия

3. Reporting и Data Warehouse

Если нужно собрать данные из разных систем для аналитики:

@Component
public class ReportingIntegration {
    @Scheduled(fixedRate = 3600000) // каждый час
    public void syncDataToWarehouse() {
        List<Order> orders = operationalDb.getAllOrders();
        List<User> users = accountingDb.getAllUsers();
        
        // Оба набора идут в reporting_db
        warehouseRepository.saveOrders(orders);
        warehouseRepository.saveUsers(users);
    }
}

Это допустимо, потому что:

  • Reporting data может быть eventual consistent (с delay)
  • Isolation между operational и reporting
  • Не влияет на production performance

4. Миграция системы (временно)

Когда переводишь data с одной системы на другую постепенно:

// Фаза 1: Dual-write
@Component
public class MigrationService {
    public void createUser(User user) {
        legacyDb.insertUser(user);     // старая система
        modernDb.insertUser(user);     // новая система
    }
}

// Фаза 2: Validation (смотрим, что данные совпадают)

// Фаза 3: Switch reads
// Читаем из modernDb, пишем в оба

// Фаза 4: Stop writing to legacy
// Полный переход

Это временное решение, но оно минимизирует риск.

Когда НЕ использовать

Хорошие альтернативы

1. REST API (или gRPC)

// Правильно: сервис A вызывает сервис B
@RestController
public class OrderController {
    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest req) {
        Order order = orderService.create(req);
        billingClient.createInvoice(order); // API call
        return order;
    }
}

Плюсы: слабая связь (loose coupling), легко тестировать, масштабируется.

2. Message Queue (Kafka, RabbitMQ)

@Component
public class OrderCreatedListener {
    @KafkaListener(topics = "orders")
    public void onOrderCreated(Order order) {
        billingService.createInvoice(order);
    }
}

Плюсы: eventual consistency, асинхронность, отказоустойчивость.

3. Event Sourcing

// Event Store — источник истины
public class OrderEventStore {
    public void append(OrderEvent event) {
        database.insertEvent(event); // история всех изменений
    }
    
    public Order getState(String orderId) {
        // Воспроизводим все события
        return events.stream()
            .filter(e -> e.getOrderId().equals(orderId))
            .reduce(new Order(), (acc, evt) -> acc.apply(evt));
    }
}

Плюсы: полная история, можно перестроить состояние, perfect audit trail.

Правило большого пальца

СценарийПодходПричина
МикросервисыAPI / Message QueueНезависимость, масштабируемость
Один бизнес-доменShared databaseКонсистентность
Real-time синхронизация (финансы)БД + ACID транзакцииГарантированная консистентность
Асинхронные событияKafka / Event StoreОтказоустойчивость, история
АналитикаReporting DB (ETL)Isolate reporting из operational
Legacy интеграцияTemporal Database IntegrationМинимум изменений в legacy

Практический пример: когда я использовал

В проекте с финансовыми транспортами между счётами:

// Две системы: Billing и Accounting
// Оба имеют таблицу transactions в shared PostgreSQL

@Repository
public class SharedTransactionRepository {
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void recordTransfer(long fromId, long toId, BigDecimal amount) {
        // Atmoic: обе системы видят одновременно
        jdbcTemplate.update(
            "UPDATE accounts SET balance = balance - ? WHERE id = ? FOR UPDATE",
            amount, fromId);
        
        jdbcTemplate.update(
            "UPDATE accounts SET balance = balance + ? WHERE id = ? FOR UPDATE",
            amount, toId);
        
        // If anything fails — full rollback
    }
}

Почему это сработало:

  • Обе системы контролирует одна команда
  • Масштаб маленький (< 100 транспортов/сек)
  • Гарантия ACID критична
  • Shared table снижает complexity vs Saga pattern

Вывод

Используй БД интеграцию ТОЛЬКО для:

  1. Legacy систем без API (временно)
  2. Одного bounded context с высокой связанностью
  3. Reporting/DW (с isolation)
  4. Операций, требующих ACID (финансы)

По умолчанию: используй REST API или Message Queue. Это лучшая практика в 95% случаев.