DDD + REST + Unit Testing
От: Stalker. Австралия  
Дата: 12.06.17 07:05
Оценка:
Хочется прояснить несколько вопросов по указанной связке и по архитектурным вопросам в целом.

1) Во-первых DDD + REST, каким образом они вообще обьединяются? Что-то ничего нормального не нагугливается, приводятся тривиальные примеры get/post/put/delete где user/id=5 и никаких примеров из реальной жизни. Как сделать в REST что-то из рязряда GetAllUsersWhoWasBornOnMonday(), ActivateUser(int id), SelectCardsWithZeroBalance(), LoadUserDetailsPage(), AssignUserToGroup(int userID, int groupID)

2) Как производится тестирование слоя DDD и сервисов. Скажем есть у нас домейн класс Person

Person
{
public string Name {get;set}
public string Surname {get;set}
public Date DOB {get;set};
public CalculateAge()
}


и есть сервис который дергается из REST

UserWorkflow
{
   public bool SendUserPromotion(int userID)
    {
      if (userID < 0)
        throw new InvalidUserID();

      User user = UserRepository.GetUser(userID);
      
      if (user == null)
        throw new InvalidUser();

      if (user.CalculateAge() > 18)     
      {
        EmailService.SendPromotionEmail(userID);
        LogService.LogCommunition(userID, "Promotion was send");
        UserRepository.UpdateUserLastPromotionDate(userID);
        return true;
      }
       else
      {
        LogService.LogError(userID, "Promotion was not send, user is too young");       
        return false;
      }
    
}


Понятно, что тут для юнит-тестирования следует замокить email service и repository, однако есть пара вопросов:

— Как проверить что EmailService, LogService и UserRepository были вызваны с нужными параметрами (юзер старше 18 лет) или не вызваны?
— Что делать собственно с домейн классом Person, если я его напрямую использую в тесте сервиса, то это вроде уже не юнит тест будет, а интеграционный, а мокить Person через интерфейс будет откровенный ужас и оверкилл
— Стоит-ли тестировать Person.CalculateAge() метод в этом случае

— Обьединяя с вопросом номер 1 — как будет SendUserPromotion() операция выглядить через интерфейс REST?


3) Вопрос по Репозиторию. Очевидно его интерфейс быстро обрастает методами UpdateUserLastPromotionDate(), GetAllUsersWhoWasBornOnMonday() и AssignUserToGroup(). Какие существуют методы по структуризации всего этого, что-бы его класс не превращался в свалку из десятков таких разношерстных методов?
Скажу сразу я знаю о существовании идеи не использовать репозиторий вообще, а прямо в методе сервиса писать соответствующие EF запросы, однако такие идеи хороши в теории, а на практике приводят к лапше из кода бизнес-логики, запросов к данным и конвертации модели данных к бизнес-моделям, а самое главное для написания юнит-тестов всего этого проходится мокить сам EF контекст, что отнимает жуткое количество времени, и позволяет тестить только простейшие запросы
Отредактировано 12.06.2017 23:15 Stalker. . Предыдущая версия .
Re: DDD + REST + Unit Testing
От: itslave СССР  
Дата: 12.06.17 12:34
Оценка:
Здравствуйте, Stalker., Вы писали:

S>Хочется прояснить несколько вопросов по указанной связке и по архит

S>- Как проверить что EmailService, LogService и UserRepository были вызваны с нужными параметрами (юзер старше 18 лет) или не вызваны?
Нормальные либы для мокинга дают возможность провалидировать аргументы замоканных сервисов из коробки.
S>- Что делать собственно с домейн классом Person, если я его напрямую использую в тесте сервиса, то это вроде уже не юнит тест будет, а интеграционный, а мокить Person через интерфейс будет откровенный ужас и оверкилл
До тех пор, пока это будет анемичный доменный класс, мокать будет по сути нечего(изменение полей можно отследить по вызовам в других методах)
S>- Стоит-ли тестировать Person.CalculateAge() метод в этом случае
Стоит вынести всю бизнес логику в сервисы и тестировать ее. Впрочем хочу отметить, что Person.CalculateAge(), при наличии Person.BirthDate — это логика представления и ее выносить не надо. Отдельный юнит тест(ы) для этого можно написать безо всяких моков.

S>- Обьединяя с вопросом номер 1 — как будет SendUserPromotion() операция выглядить через интерфейс REST?

например /workflow/user/id/promotion

S>3) Вопрос по Репозиторию. Очевидно его интерфейс быстро обрастает методами UpdateUserLastPromotionDate(), GetAllUsersWhoWasBornOnMonday() и AssignUserToGroup(). Какие существуют методы по структуризации всего этого, что-бы его класс не превращался в свалку из десятков таких разношерстных методов?

В нормальных языках программирования есть linq.
Хотя его юнит тестировать — не самая приятная штука.
Re: DDD + REST + Unit Testing
От: GarryIV  
Дата: 12.06.17 21:59
Оценка:
Здравствуйте, Stalker., Вы писали:

S>Хочется прояснить несколько вопросов по указанной связке и по архитектурным вопросам в целом.


S>1) Во-первых DDD + REST, каким образом они вообще обьединяются? Что-то ничего нормального не нагугливается, приводятся тривиальные примеры get/post/put/delete где user/id=5 и никаких примеров из реальной жизни. Как сделать в REST что-то из рязряда GetAllUsersWhoWasBornOnMonday(), ActivateUser(int id), SelectCardsWithZeroBalance(), LoadUserDetailsPage(), AssignUserToGroup(int userID, int groupID)


Гугли CQRS. Отдельно модель для команд, отдельно для запросов.

S>2) Как производится тестирование слоя DDD и сервисов. Скажем есть у нас домейн класс Person



S>и есть сервис который дергается из REST


S>Понятно, что тут для юнит-тестирования следует замокить email service и repository, однако есть пара вопросов:


S>- Как проверить что EmailService, LogService и UserRepository были вызваны с нужными параметрами (юзер старше 18 лет) или не вызваны?

Читайте доки к библиотеки мокирования. Например в мокито есть verify методы.
WBR, Igor Evgrafov
Re[2]: DDD + REST + Unit Testing
От: Stalker. Австралия  
Дата: 12.06.17 22:51
Оценка:
Здравствуйте, itslave, Вы писали:

I>Стоит вынести всю бизнес логику в сервисы и тестировать ее. Впрочем хочу отметить, что Person.CalculateAge(), при наличии Person.BirthDate — это логика представления и ее выносить не надо. Отдельный юнит тест(ы) для этого можно написать безо всяких моков.


Person.CalculateAge() это пример, там будут более сложные методы. Про существование анемичного подхода я в курсе, но во-первых нам все-же нужен обычный DDD, во-вторых даже в случае анемичного подхода проблема остается на месте — Person превратится в какой-нибудь PeopleService и каким-то образом должен будет юнит-тестится совместно с моим PersonService из примера.
По-сути вопрос можно свести к тому, что если я напишу юнит-тесты для логики Person, то тестирование PersonService возможно только в виде интеграционного теста т.к. Person нельзя замокить (не выделять-же DDD модели в интерфейсы для этого). Нормально-ли это


I>В нормальных языках программирования есть linq.

I>Хотя его юнит тестировать — не самая приятная штука.

Мы используем EF, отсюда и вопрос. Даже если забыть про лапшу в коде, юнит-тестирование таких классов настоящий кошмар, у меня сейчас на совершенно базовые методы типа добавить или удалить пользователя создается moq setup юнит теста размером раз в 10 превышающий код собственно метода и польза этих тестов равна по-сути нулю т.к. можно замокить только совершенно базовые запросы.
Re[3]: DDD + REST + Unit Testing
От: AndrewJD США  
Дата: 12.06.17 23:05
Оценка:
Здравствуйте, Stalker., Вы писали:

S>Person.CalculateAge() это пример, там будут более сложные методы. Про существование анемичного подхода я в курсе, но во-первых нам все-же нужен обычный DDD, во-вторых даже в случае анемичного подхода проблема остается на месте — Person превратится в какой-нибудь PeopleService и каким-то образом должен будет юнит-тестится совместно с моим PersonService из примера.

S>По-сути вопрос можно свести к тому, что если я напишу юнит-тесты для логики Person, то тестирование PersonService возможно только в виде интеграционного теста т.к. Person нельзя замокить (не выделять-же DDD модели в интерфейсы для этого). Нормально-ли это


В случае анемичного подхода у тебя будет класс Person в котором будут только данные без логики (там не будет никаких CalculateAge, Validate и т.д.) и PersonService с бизнес логикой. В данном подходе нужно мокать зависимости только PersonService, если они есть. Если их нет, то и мокать ничего не надо.

>но во-первых нам все-же нужен обычный DDD

Зачем нужен?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[4]: DDD + REST + Unit Testing
От: Stalker. Австралия  
Дата: 12.06.17 23:35
Оценка:
Здравствуйте, AndrewJD, Вы писали:

AJD>В случае анемичного подхода у тебя будет класс Person в котором будут только данные без логики (там не будет никаких CalculateAge, Validate и т.д.) и PersonService с бизнес логикой. В данном подходе нужно мокать зависимости только PersonService, если они есть. Если их нет, то и мокать ничего не надо.


Я сейчас переименовал класс в примере в UserWorkflow что-бы не путать с UserService анемичного подхода. Мой вопрос заключался в том, как будет тестироваться метод UserWorkflow.SendUserPromotion() вне зависимости анемичный это подход или нет. В случае DDD у меня существует зависимость Person.CalculateAge(), в анемичном подходе будет PersonService.CalculateAge(), т.е. тестирование становится интеграционным, т.к. замокать эти классы нельзя, они не вынесены в интерфейсы, соответственно кто как к этому подходит?
Обращу внимание, на разницу между UserWorkflow и бизнес-логикой в Person т.к. это возможно кого-то путает, класс UserWorkflow не содержит бизнес логику, он содержит workflow логику по манипуляции разными сервисами, типа репозитория, сервиса рассылки почты, какой-нибудь криптографии, логирования и прочего. Все это надо тоже тестировать, и все эти сервисы могут быть замокены, за исключением самого Person, т.к. он часть DDD и соответственно не мокится, откуда и вопрос
DDD используется т.к. все хотя-бы знают что это такое, анемичный подход известен только на этом форуме
Re[5]: DDD + REST + Unit Testing
От: AndrewJD США  
Дата: 13.06.17 00:05
Оценка:
Здравствуйте, Stalker., Вы писали:

S>Я сейчас переименовал класс в примере в UserWorkflow что-бы не путать с UserService анемичного подхода. Мой вопрос заключался в том, как будет тестироваться метод UserWorkflow.SendUserPromotion() вне зависимости анемичный это подход или нет. В случае DDD у меня существует зависимость Person.CalculateAge(), в анемичном подходе будет PersonService.CalculateAge(), т.е. тестирование становится интеграционным, т.к. замокать эти классы нельзя, они не вынесены в интерфейсы, соответственно кто как к этому подходит?


Какая разница называется оно формально интеграционным или юнит тестированием? Суть одна, на вход подаются входные данные сценария, на выходе проверяется результат, а также что моки соотвествующих сервисов были вызваны (email, DB, etc).
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[6]: DDD + REST + Unit Testing
От: Stalker. Австралия  
Дата: 13.06.17 01:04
Оценка:
Здравствуйте, AndrewJD, Вы писали:

AJD>Какая разница называется оно формально интеграционным или юнит тестированием? Суть одна, на вход подаются входные данные сценария, на выходе проверяется результат, а также что моки соотвествующих сервисов были вызваны (email, DB, etc).


разница важная, т.к. интеграционный тест будет также завязан на логику класса DDD, у которой есть свои юнит-тесты. Но ок, ваш поинт понятен — тест становится интеграционным
Re[3]: DDD + REST + Unit Testing
От: itslave СССР  
Дата: 13.06.17 05:36
Оценка:
Здравствуйте, Stalker., Вы писали:

S>Person.CalculateAge() это пример, там будут более сложные методы. Про существование анемичного подхода я в курсе, но во-первых нам все-же нужен обычный DDD

Обычный DDD был придуман лет эдак за 10 до юнит тестирования, и как следствие — он не юнит тестируется в общем случае.

S>во-вторых даже в случае анемичного подхода проблема остается на месте — Person превратится в какой-нибудь PeopleService и каким-то образом должен будет юнит-тестится совместно с моим PersonService из примера.

S>По-сути вопрос можно свести к тому, что если я напишу юнит-тесты для логики Person, то тестирование PersonService возможно только в виде интеграционного теста т.к. Person нельзя замокить (не выделять-же DDD модели в интерфейсы для этого). Нормально-ли это
У тебя останется анемичный Person, который не мокается, и 2 сервиса — PeopleService + PersonService, которые тестируются по отдельности. И если надо — интеграционный тест на все хозяйство вместе.

S>Мы используем EF, отсюда и вопрос. Даже если забыть про лапшу в коде, юнит-тестирование таких классов настоящий кошмар, у меня сейчас на совершенно базовые методы типа добавить или удалить пользователя создается moq setup юнит теста размером раз в 10 превышающий код собственно метода и польза этих тестов равна по-сути нулю т.к. можно замокить только совершенно базовые запросы.

С CUD вообще проблем нет, а вот с селектами — да, надо помучиться. На сайте майкрососфта есть пошаговое руководство. Альтернатива — инкапсулирование селектов в неюниттестируемые репозитарии, по одному методу на селект — тоже не сказать чтобы супер.
Re[5]: DDD + REST + Unit Testing
От: itslave СССР  
Дата: 13.06.17 05:38
Оценка:
Здравствуйте, Stalker., Вы писали:

S>Мой вопрос заключался в том, как будет тестироваться метод UserWorkflow.SendUserPromotion() вне зависимости анемичный это подход или нет.

Кэп подсказывает, что стратегия тестирования зависит от имплементации.
Re[4]: DDD + REST + Unit Testing
От: Stalker. Австралия  
Дата: 13.06.17 08:34
Оценка:
Здравствуйте, itslave, Вы писали:

I>У тебя останется анемичный Person, который не мокается, и 2 сервиса — PeopleService + PersonService, которые тестируются по отдельности. И если надо — интеграционный тест на все хозяйство вместе.


мокить PersonService? В результате передавать в UserWorkflow охапку интерфейсов всех сервисов обслуживающих анемичную модель? Их-же может реально много оказаться, я уже представляю чем все это закончится — вместо множества небольших сервисов разработчики начнут лепить сборные солянки из методов т.к. незахотят связываться с созданием нового сервиса для какого-то случая, мокингом и передачей его в кучу юнит-тестов. Либо просто втухушку будут лепить новый метод в неправильный сервис просто потому что тот уже замокен что-бы не заморачиваться с передачей нового сервиса в workflow.
В общем я так понимаю в случае DDD юнит тесты для классов использующих домейн-модели не пишут, а делают интеграционные тесты. Ок


I>С CUD вообще проблем нет, а вот с селектами — да, надо помучиться. На сайте майкрососфта есть пошаговое руководство. Альтернатива — инкапсулирование селектов в неюниттестируемые репозитарии, по одному методу на селект — тоже не сказать чтобы супер.


Для CUD одной записи еще ничего, но если их несколько (скажем при удалении пользователя удалить список его контактов из другой таблицы), то код уже становится монстрообразным, в Moq для проверки таких вещей надо использовать коллбэки, где вручную искать конкретную удаляемую запись в коллекции и удалять их все по-очереди т.к. Moq не позволяет удалить строку из контекста, эту операцию надо симулировать в коллбэке на своей собственной коллекции. Вообще код слоя репозитория по этой причине никто не мокит, а если-уж сильно хочется — то просто пишутся интеграционные тесты с настоящей БД, которые заодно могут тестировать куда более интересные вещи чем простые селекты или делиты, например так можно тестить проверку уникальности записи в таблице, транзакционную согласованность и кучу всего прочего, причем затраченное время будет меньше чем если-бы писался код мокинга для EF
Re[5]: DDD + REST + Unit Testing
От: itslave СССР  
Дата: 13.06.17 09:00
Оценка:
Здравствуйте, Stalker., Вы писали:

S>мокить PersonService? В результате передавать в UserWorkflow охапку интерфейсов всех сервисов обслуживающих анемичную модель? Их-же может реально много оказаться, я уже представляю чем все это закончится — вместо множества небольших сервисов разработчики начнут лепить сборные солянки из методов т.к. незахотят связываться с созданием нового сервиса для какого-то случая, мокингом и передачей его в кучу юнит-тестов.

Да, будет какое кол-во сервисов, заинджекченные в воркфлоу или куда там еще. В самых запущенных случаях видел 20+ в параметрах конструктора. За такое надо бить линейкой по рукам на код ревью и вуаля. Также хочу отметить, что говнокод можно написать в любом случае, и я также неоднократно видел превращения aggregation roots в многотысячострочные монстры.

S>В общем я так понимаю в случае DDD юнит тесты для классов использующих домейн-модели не пишут, а делают интеграционные тесты. Ок

Да. Ведь в классике DDD даже методы Load/Save должны быть заимплеменчены частью бизнес обьекта, куда тут юнит тесты.
С интеграционными тестами все хорошо кроме 3х вещей
— необходимости наличия сконфигурированного енва для их запуска(особенно актуально в случае интеграций со всякими другими сервисами)
— достаточно приличного(как правило — нескольких часов) времени прогона полного сета тестов.
— в разы больших эфортов на написание-рефакторинг тестов
Все это неизбежно приводит к
— менее полному покрытию бизнес логики тестами
— более позднему обнаружению багов и как следствие — нестабильным билдам
Если эти риски тебя не настораживают — то почему нет.

S>Для CUD одной записи еще ничего, но если их несколько (скажем при удалении пользователя удалить список его контактов из другой таблицы), то код уже становится монстрообразным, в Moq для проверки таких вещей надо использовать коллбэки, где вручную искать конкретную удаляемую запись в коллекции и удалять их все по-очереди т.к. Moq не позволяет удалить строку из контекста, эту операцию надо симулировать в коллбэке на своей собственной коллекции. Вообще код слоя репозитория по этой причине никто не мокит, а если-уж сильно хочется — то просто пишутся интеграционные тесты с настоящей БД, которые заодно могут тестировать куда более интересные вещи чем простые селекты или делиты, например так можно тестить проверку уникальности записи в таблице, транзакционную согласованность и кучу всего прочего, причем затраченное время будет меньше чем если-бы писался код мокинга для EF


Вообщето юнит тестами рекомендуют покрывать исключительно бизнес логику, репохзитарий же к ней не относится — достаточно проверить что произошел вызов нужного метода с нужными параметрами.

И да. Юнит тесты не заменяют интеграционные, а дополняют.
Re[6]: DDD + REST + Unit Testing
От: Stalker. Австралия  
Дата: 13.06.17 09:30
Оценка:
Здравствуйте, itslave, Вы писали:

I>Да, будет какое кол-во сервисов, заинджекченные в воркфлоу или куда там еще. В самых запущенных случаях видел 20+ в параметрах конструктора. За такое надо бить линейкой по рукам на код ревью и вуаля. Также хочу отметить, что говнокод можно написать в любом случае, и я также неоднократно видел превращения aggregation roots в многотысячострочные монстры.


так будет либо 20+ параметров на сложные операции, либо сборные солянки, никуда при таком подходе от этого не деться


I>С интеграционными тестами все хорошо кроме 3х вещей

I> — необходимости наличия сконфигурированного енва для их запуска(особенно актуально в случае интеграций со всякими другими сервисами)
I> — достаточно приличного(как правило — нескольких часов) времени прогона полного сета тестов.
I> — в разы больших эфортов на написание-рефакторинг тестов

в моем случае тест получается "интеграционным" только потому, что класс workflow завязан на модель DDD, все остальные сервисы (типа репозитория или почтового сервиса) там будут замокены, так что все будет быстро. Покрытие логики там тоже будет достаточным, ведь модель DDD будет покрыта своими юнит тестами. По-настоящему "интеграционным" будут только тесты кода репозитория, и то если мы вообще захотим с ними связываться, т.к. там тесты будут гонятся с настоящей базой данных.
Re[7]: DDD + REST + Unit Testing
От: itslave СССР  
Дата: 13.06.17 09:56
Оценка: +1
Здравствуйте, Stalker., Вы писали:

S>Здравствуйте, itslave, Вы писали:


I>>Да, будет какое кол-во сервисов, заинджекченные в воркфлоу или куда там еще. В самых запущенных случаях видел 20+ в параметрах конструктора. За такое надо бить линейкой по рукам на код ревью и вуаля. Также хочу отметить, что говнокод можно написать в любом случае, и я также неоднократно видел превращения aggregation roots в многотысячострочные монстры.


S>так будет либо 20+ параметров на сложные операции, либо сборные солянки, никуда при таком подходе от этого не деться

Анемичная модель тяготеет к множеству мелких сервисам, православная "rich DDD model" — к god aggregation root. Жизнь — боль

S>в моем случае тест получается "интеграционным" только потому, что класс workflow завязан на модель DDD, все остальные сервисы (типа репозитория или почтового сервиса) там будут замокены, так что все будет быстро. Покрытие логики там тоже будет достаточным, ведь модель DDD будет покрыта своими юнит тестами. По-настоящему "интеграционным" будут только тесты кода репозитория, и то если мы вообще захотим с ними связываться, т.к. там тесты будут гонятся с настоящей базой данных.

Опять таки, юнит тесты придумали такими потому что так удобней. В общем случае удобней замокать зависимости, чем конфигурировать целые деревья обьектов, а если они еще и statefull, то выводить их в нужное состояние перед стартом теста. Хотя Возможно в твоем случае можно сделать проще или после первой пачки тестов задумаешься и сделаешь "как рекомендуют".
Re[7]: DDD + REST + Unit Testing
От: AndrewJD США  
Дата: 13.06.17 14:31
Оценка:
Здравствуйте, Stalker., Вы писали:


S>так будет либо 20+ параметров на сложные операции, либо сборные солянки, никуда при таком подходе от этого не деться


Если твой workflow использует эти 20+ сервисов — то этого не избежать. Но зато получаем полное покрытие сценария тестом. Как показывает практика основные ошибки возникают при взаимодействии компонентов между собой.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[6]: DDD + REST + Unit Testing
От: AndrewJD США  
Дата: 13.06.17 14:44
Оценка: +2
Здравствуйте, itslave, Вы писали:

I> — необходимости наличия сконфигурированного енва для их запуска(особенно актуально в случае интеграций со всякими другими сервисами)

I> — достаточно приличного(как правило — нескольких часов) времени прогона полного сета тестов.
Это если полноценный интеграционный тест. Если же под интеграционным понимать только тестирование несколько компонентов и использованием моков и unit test инфрастуктуры — этой проблемы нет.

I> — в разы больших эфортов на написание-рефакторинг тестов

Не согласен. Если использовать blackbox подход — то как раз время сокращается при рефакторинге, т.к. внешние интерфейсы обычно гораздо стабильнее чем их реализация. В некоторых случаях можно хранить тествовые сценарии и данные во внешних XML/JSON файлах. Время подготовки тестовых данных наверное будет больше. Но такой тест покрывает весь сценарий + появляется возможность как угодно рефакторить реализацию — тест не меняется.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[7]: DDD + REST + Unit Testing
От: itslave СССР  
Дата: 13.06.17 15:41
Оценка:
Здравствуйте, AndrewJD, Вы писали:

AJD>Это если полноценный интеграционный тест. Если же под интеграционным понимать только тестирование несколько компонентов и использованием моков и unit test инфрастуктуры — этой проблемы нет.

Остается необходимость очень аккуратно работать с сервисами, которые работают со внешними ресурсами, в первую очередь — с базой данных.

I>> — в разы больших эфортов на написание-рефакторинг тестов

AJD>Не согласен. Если использовать blackbox подход — то как раз время сокращается при рефакторинге, т.к. внешние интерфейсы обычно гораздо стабильнее чем их реализация. В некоторых случаях можно хранить тествовые сценарии и данные во внешних XML/JSON файлах. Время подготовки тестовых данных наверное будет больше. Но такой тест покрывает весь сценарий + появляется возможность как угодно рефакторить реализацию — тест не меняется.
Даже с blackbox, все очень сильно зависит от того, насколько сервисы stateful и насколько дерево сложное. Хранение состояния во внешних файлах приводит к гемору c версионированием.
Re: DDD + REST + Unit Testing
От: Vladek Россия Github
Дата: 21.06.17 01:52
Оценка: +1
Здравствуйте, Stalker., Вы писали:

S>Хочется прояснить несколько вопросов по указанной связке и по архитектурным вопросам в целом.


S>1) Во-первых DDD + REST, каким образом они вообще обьединяются? Что-то ничего нормального не нагугливается, приводятся тривиальные примеры get/post/put/delete где user/id=5 и никаких примеров из реальной жизни. Как сделать в REST что-то из рязряда GetAllUsersWhoWasBornOnMonday(), ActivateUser(int id), SelectCardsWithZeroBalance(), LoadUserDetailsPage(), AssignUserToGroup(int userID, int groupID)


S>2) Как производится тестирование слоя DDD и сервисов. Скажем есть у нас домейн класс Person


S>- Как проверить что EmailService, LogService и UserRepository были вызваны с нужными параметрами (юзер старше 18 лет) или не вызваны?

S>- Что делать собственно с домейн классом Person, если я его напрямую использую в тесте сервиса, то это вроде уже не юнит тест будет, а интеграционный, а мокить Person через интерфейс будет откровенный ужас и оверкилл
S>- Стоит-ли тестировать Person.CalculateAge() метод в этом случае
S>- Обьединяя с вопросом номер 1 — как будет SendUserPromotion() операция выглядить через интерфейс REST?

Пример кода — это не DDD, даже не ООП, это процедурное программирование. Это вызов набора процедур, оформленных в виде методов в разных классах.

S>3) Вопрос по Репозиторию. Очевидно его интерфейс быстро обрастает методами UpdateUserLastPromotionDate(), GetAllUsersWhoWasBornOnMonday() и AssignUserToGroup().


Нет, не обрастает. Репозиторий где-то хранит объекты между запусками программы и умеет их оттуда доставать. Это два-три метода Add/Remove/Find.

S>Какие существуют методы по структуризации всего этого, что-бы его класс не превращался в свалку из десятков таких разношерстных методов?


Классический ООП. Решение задачи проектируется в виде набора объектов, которые посылают друг другу сообщения (вызывают методы). Это модель предметной области задачи. На одну задачу их может быть много, в DDD каждую такую отдельную модель называют bounded context. И никаких следов баз данных и EF в этой модели нет. Чтобы освободиться от призрака бд, полезно представлять, что оперативная память бесконечна. Это сразу ставит бд на правильное место в твоей системе ценностей.

Пробуждать модель предметной области имеет смысл только когда нужно внести изменения в систему, выполнить операции на запись. Для чтения модель предметной области можно не использовать. Не нужно, если всё сводится к ковырянию в БД с минимумом проверок перед записью.

S>Скажу сразу я знаю о существовании идеи не использовать репозиторий вообще, а прямо в методе сервиса писать соответствующие EF запросы, однако такие идеи хороши в теории, а на практике приводят к лапше из кода бизнес-логики, запросов к данным и конвертации модели данных к бизнес-моделям, а самое главное для написания юнит-тестов всего этого проходится мокить сам EF контекст, что отнимает жуткое количество времени, и позволяет тестить только простейшие запросы


Не нужно мокать EF, не нужно тестировать способность EF обращаться к бд и заполнять структуры данными. Этим занимается команда по разработке EF и она уже выложила EF в продакшен — значит, тесты у них выполняются успешно и тебе не надо их писать. Если база данных заменяет собой модель предметной области, репозитории и юнит-тесты не нужны. Надо просто использовать EF и следить за соединением к бд. Код получится простой и короткий.
Re[2]: DDD + REST + Unit Testing
От: Stalker. Австралия  
Дата: 22.06.17 23:06
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Пример кода — это не DDD, даже не ООП, это процедурное программирование. Это вызов набора процедур, оформленных в виде методов в разных классах.


потому-что приведенный пример кода — это сервис, не имеющий отношения к бизнес-логике и ООП

V>Нет, не обрастает. Репозиторий где-то хранит объекты между запусками программы и умеет их оттуда доставать. Это два-три метода Add/Remove/Find.


хранит, да. В базе данных. Никаких кэшей и прочих состояний т.к. речь про веб

V>Классический ООП. Решение задачи проектируется в виде набора объектов, которые посылают друг другу сообщения (вызывают методы). Это модель предметной области задачи. На одну задачу их может быть много, в DDD каждую такую отдельную модель называют bounded context. И никаких следов баз данных и EF в этой модели нет. Чтобы освободиться от призрака бд, полезно представлять, что оперативная память бесконечна. Это сразу ставит бд на правильное место в твоей системе ценностей.


Вопрос был не про ООП, а интефейс репозитория, это совершенно разные вещи

V>Не нужно мокать EF, не нужно тестировать способность EF обращаться к бд и заполнять структуры данными. Этим занимается команда по разработке EF и она уже выложила EF в продакшен — значит, тесты у них выполняются успешно и тебе не надо их писать. Если база данных заменяет собой модель предметной области, репозитории и юнит-тесты не нужны. Надо просто использовать EF и следить за соединением к бд. Код получится простой и короткий.


EF не надо мокать, а вот репозиторий надо, этот вопрос проистекал из вопроса про его интерфейс
Re[3]: DDD + REST + Unit Testing
От: Vladek Россия Github
Дата: 23.06.17 05:22
Оценка:
Здравствуйте, Stalker., Вы писали:

S>EF не надо мокать, а вот репозиторий надо, этот вопрос проистекал из вопроса про его интерфейс


Это не репозиторий. То, что ты понимаешь под репозиторием, называется шлюзом — https://martinfowler.com/eaaCatalog/gateway.html А ещё точнее, шлюзом к табличным данным: https://martinfowler.com/eaaCatalog/tableDataGateway.html Проблемы, которую он решает (возня с SQL в коде) — у тебя нет, у тебя есть EF и запросы LINQ.

Твой репозиторий возвращает живые сущности EF? Если да, то значит ты не стесняешься светить EF по всей системе, а следовательно лишний код в виде репозиториев или шлюзов не нужен — проще инжектить контекст EF куда надо и использовать напрямую без всяких обёрток. Нет репозиториев и шлюзов, не нужно ничего мокать и писать тесты.

С другой стороны, интерфейс шлюза, если ты его оставишь, действительно зарастёт методами. Они все собраны в одном классе потому, что скрывают за собой весь SQL. Твой шлюз будет скрывать контекст EF. Однако, опять же, если методы возвращают сущности из этого контекста, то никакого сокрытия на самом деле нет.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.