Коллеги, поделитесь мнением и мудростью!
Не нарушает ли Фаулеровское определение Unit Of Work принцип Single Responsibility из принципов ООД сформулированных Р. Мартином еще лет 20 назад. Вопрос абсолютно не о сферических конях в вакууме, конкретную проблему смотрите ниже.
Немного теории
У Фаулера UoW определяется как:
"Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems."
У Мартина Single Responsibility:
"A class should have one, and only one, reason to change."
Т.е. получается, если слепо следуем Фоулеру, нарушаем принцип Мартина, так как если меняется метод отслеживания изменений — меняем UofW, если меняется координация записывания изменений — опять меняем UoW.
Пример из практики
Смотрим на взаимодействие репозитория и Unit Of Work. Пусть у нас есть один репозиторий реализующий IUserRepository, который может работать с каким-то хранилищем данных (база, мок объекты, файловая система, компонента написанная PupkinSoft Inc., неважно). Ну, и допустим мы хотим сделать такое отслеживание изменений о котором пишет Фаулер.
Внимание вопрос, если какая-то из реализаций репозитория уже делает “list of objects affected by a business transaction”, зачем мне еще раз это реализовывать в UnitOfWork? Но Фаулер это жестко запихивает в UnitOfWork. Это на самом порождает еще больше проблем. Например если я отслеживаю изменения в UnitOfWork, то если я меняю данные, а потом зачитываю новые данные, то кто-то еще должен проверить не содержатся ли зачитанные данные в списке измененных и т.д.
Чешем затылок, мыслим по Мартину. По нему должны быть классы которые:
1. Coordinates the writing out of changes and the resolution of concurrency problems (IUnitOfCoordinationWork)
2. Repository (чтобы он там не делал, не о нем вопрос) (IUserRepository)
3. Maintains a list of objects affected by a business transaction (реализует ITrackChanges)
Тогда все просто и красиво.
1. UnitOfWork реализует IUnitOfCoordinationWork.
2. Методы хранилища реализуют IRepository
3. Если конкретная реализация репозитория реализует “list of objects affected by a business transaction” то оборачиваем эту функциональность в ITrackChanges, если нет, то пишем сами. Т.е. если функциональность уже есть в репозитории, то не лопатим тонны кода делающего тоже самое в UnitOfWork, изобретая велосипед, как это было если б делали по Фаулеру, а используем готовое.
Да и вообще, но это другая тема, “Maintains a list of objects affected by a business transaction” может быть нафиг не нужна на многих проектах. Зачем добавлять это требование в такой “краеугольный камень” как UnitOfWork?
Вот такие мысли. Поскольку я “замахнулся” на великого Фаулера, то мне интересно, что думаете вы.
Спасибо!