Где проходят границы транзакций в DDD
В каком слое архитектуры DDD следует располагать код управления транзакциями? Есть ли какие-либо рекомендации и best-practies на этот счет?
На данный момент я располагаю код управления транзакциями в репозиториях - внутри каждого метода репозитория располагается код отвечающий за открытие и закрытие транзакции.
Ответы (1 шт):
У вас неправильно проходит граница транзакций. Контроль целостности и бизнес-правил в DDD делается на уровне агрегата. Операция выглядит так:
- читаем агрегат из репозитория
- выполняем операцию над агрегатом
- сохраняем агрегат в репозиторий
Вот эти три действия должны выполнятся в одной транзакции. Этот код находится в слое приложения (Application Layer). Т.е. есть класс типа:
class AccountApp {
public void topUpAccount(Long accountId, Long amount) {
AccountAggregate account = accountRepository.get(accountId);
account.topUp(amount);
accountRepository.save(account);
}
}
Метод topUpAccount должен выполнятся в одной транзакции. Обратите внимание, что слой бизнес-логики в данном случае это только AccountAggregate (сам метод topUp может быть довольно сложным и использовать другие сущности из агрегата Account). И AccountAggregate ничего не знает ни о том:
- когда транзакция начинается/заканчивается
- как именно реализована транзакция
В идеале сама демаркация транзакций должна быть в слое приложения, а реализация отдельно (в слое инфраструктуры).
Вот пример того, как это делается в spring:
class AccountApp {
@Transactional
public void topUpAccount(Long accountId, Long amount) {
...
}
}
Тут @Transactional обозначает, что метод topUpAccount будет выполнятся в транзакции. Как именно это делается, даже класс AccountApp не знает (не говоря уже об слое домена). Собственно реализация транзакций находится в сгенерированном прокси, который оборачивает вызовы к topUpAccount. Этот прокси умеет взаимодействовать в repository определенным образом (обычно через паттерн UnitOfWork).