DDD, архитектура и т.д.
От: SharpDeveloper  
Дата: 14.11.14 14:31
Оценка:
Чуствую себя обманутым.
DAL<->BL<->Presentation

Конечно нам всем нужен слой доступа к данным, конечно нам нужен бизнес слой и не дай Боже слою представления вызвать метод репозитория! Конечно же (Entity Framework) нам DataContext не годится, нам надо нагородить Generic Repository и в бизнес слое только через интерфейсы IoC'чить репозитории, а посему IQueryable'ы не торчат наружу! Как же! Разве можно привязыватся к Entity Framework? Нам необходим универсальный фасад!

И вот я представляю как мне нужно юзеру просто поставить IsActive в false.
— Слой представления создаёт UserManager (разумеется ИоКи там всякие!) и вызывает метод бизнес слоя UserManager.SetActiveFlag(false)
— Бизнес слой через Иоки создает репозиторий
— Вызываем метод Update UserRepository, или даже UserRepository.SetActiveFlag
— Репозиторий создает датаконтекст и do the trick....

Куда от этого бежать?!!?!?
Я не настаиваю на открытии коннекшена прямо в контроллере и выполнения SQL запроса предварительно подготовленного StringBuilder'ом.... но как то трехзвенка меня разочаровывает.

Боян?
Re: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 14.11.14 14:36
Оценка:
Здравствуйте, SharpDeveloper, Вы писали:

SD>DAL<->BL<->Presentation


При использовании LINQ это всё не актуально.
Если нам не помогут, то мы тоже никого не пощадим.
Re: DDD, архитектура и т.д.
От: Baudolino  
Дата: 14.11.14 14:57
Оценка:
[offtopic] Первый раз задавался этим вопросом, кажется, лет пять назад, но воз и ныне там. Доколе .Net-чики будут здесь обсуждать целесообразность трехзвенки? Когда уже появятся (ну или станут общепринятыми, если они уже есть) адекватные паттерны и фреймворки, которые раз и на всегда ответят на этот вопрос?
Re: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 14.11.14 16:59
Оценка: +1
Здравствуйте, SharpDeveloper, Вы писали:

SD>Чуствую себя обманутым.

SD>DAL<->BL<->Presentation

SD>Конечно нам всем нужен слой доступа к данным, конечно нам нужен бизнес слой и не дай Боже слою представления вызвать метод репозитория! Конечно же (Entity Framework) нам DataContext не годится, нам надо нагородить Generic Repository и в бизнес слое только через интерфейсы IoC'чить репозитории, а посему IQueryable'ы не торчат наружу! Как же! Разве можно привязыватся к Entity Framework? Нам необходим универсальный фасад!


SD>И вот я представляю как мне нужно юзеру просто поставить IsActive в false.

SD>- Слой представления создаёт UserManager (разумеется ИоКи там всякие!) и вызывает метод бизнес слоя UserManager.SetActiveFlag(false)
SD>- Бизнес слой через Иоки создает репозиторий
SD>- Вызываем метод Update UserRepository, или даже UserRepository.SetActiveFlag
SD>- Репозиторий создает датаконтекст и do the trick....

SD>Куда от этого бежать?!!?!?

SD>Я не настаиваю на открытии коннекшена прямо в контроллере и выполнения SQL запроса предварительно подготовленного StringBuilder'ом.... но как то трехзвенка меня разочаровывает.

SD>Боян?


Первый ответ:
Если у вас приложение сбольшое, сложное и долго уже разрабатывается, то наличие такого разделения хоть и прибавляет работы при реализации конкретной фичи, но снижает зависимость компонентов и удешевляет поддержку.
А также уменьшает количество проблем при миграции, например, на новые версии того же EF, где могут решить поменять что-нибудь.

А если приложение маленькое и простое — то не нужно стрелять их пушек по воробьям и применять кучу паттернов и слоёв деплоймента там, где они не нужны.
Например, не очень нужен EF там, где сущностей совсем мало и схема базы меняется редко. А так же там, где сущности очень разнородные и сложные.
Не очень нужен Generic Repository в простом приложении, которое заведомо не будет никуда мигрировать.
Совсем не нужны три физически отдельных слоя, если все три будут хостится на одной машине (что не отменяет логического разделения внутри, пусть даже одной, сборки)
Совсем не нужны IoC-и когда схема связи классов простая и статическая.

Второй ответ:
Действий действительно много, но правильный каркас приложения большинство из них уже умеет делать. Поэтому это влияет только на производительность системы, но не на производительность программиста.

SD>Куда от этого бежать?!!?!?

Смотря с какой целью
Re[2]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 14.11.14 18:18
Оценка:
Здравствуйте, vmpire, Вы писали:

V>Совсем не нужны IoC-и когда схема связи классов простая и статическая.

А это почему? Как без DI писать вменяемые юнит-тесты?
Ок, если это прототип, то может и можно обойтись без юнит-тестов, но реальный проект... куда сейчас без этого?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 14.11.14 18:40
Оценка: 12 (2) +4
Здравствуйте, ., Вы писали:

V>>Совсем не нужны IoC-и когда схема связи классов простая и статическая.

.>А это почему? Как без DI писать вменяемые юнит-тесты?

DI нужен если:

1. У тебя pluggable application.
2. Приложение использует внешние сервисы, которые желательно мокать и фейкать при тестировании.
3. Ты пишешь книгу или пост в блог и хочешь выглядеть нереально крутым чуваком.

Под тесты подпадает лишь пункт 2. Но топик-стартер говорит об обычном бизнес приложении, которое массажирует какие-то там данные в БД и гоняет их туда сюда. Применение DI в таком контексте даёт как раз те самые невменяемые и никому не нужные тесты.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 14.11.14 18:47
Оценка: +1
Здравствуйте, ., Вы писали:


V>>Совсем не нужны IoC-и когда схема связи классов простая и статическая.

.>А это почему? Как без DI писать вменяемые юнит-тесты?
Вы DI с mocks и stubs не путаете?
И то и другое прекрасно реализуется без DI фреймворков. Да и не всегда нужны mocks и stubs, если тестируемая логика достаточно изолирована.
Re[4]: DDD, архитектура и т.д.
От: AndrewJD США  
Дата: 14.11.14 19:21
Оценка:
Здравствуйте, IT, Вы писали:

IT>1. У тебя pluggable application.

IT>2. Приложение использует внешние сервисы, которые желательно мокать и фейкать при тестировании.

А если приложение использует внтуренний сложный сревис? Почему бы его тоже не мокать и фейкать при тестировании?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[5]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 14.11.14 19:49
Оценка:
Здравствуйте, AndrewJD, Вы писали:

AJD>А если приложение использует внтуренний сложный сревис? Почему бы его тоже не мокать и фейкать при тестировании?


Например?
Если нам не помогут, то мы тоже никого не пощадим.
Re[6]: DDD, архитектура и т.д.
От: AndrewJD США  
Дата: 14.11.14 20:40
Оценка:
Здравствуйте, IT, Вы писали:

AJD>>А если приложение использует внутренний сложный сервис? Почему бы его тоже не мокать и фейкать при тестировании?


IT>Например?


Например, внутренний сервис который реализует сложные алгоритмические расчеты.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[7]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 14.11.14 20:52
Оценка:
Здравствуйте, AndrewJD, Вы писали:

AJD>>>А если приложение использует внутренний сложный сервис? Почему бы его тоже не мокать и фейкать при тестировании?

IT>>Например?
AJD>Например, внутренний сервис который реализует сложные алгоритмические расчеты.

Сложные или тормознутые?
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 14.11.14 21:03
Оценка: -3
Здравствуйте, IT, Вы писали:

IT>DI нужен если:


IT>1. У тебя pluggable application.

Следует читать "легко переиспользуемый код".

IT>2. Приложение использует внешние сервисы, которые желательно мокать и фейкать при тестировании.

Следует читать "зависит от других частей с нетривиальным поведением".

IT>3. Ты пишешь книгу или пост в блог и хочешь выглядеть нереально крутым чуваком.

Следует читать "не вчерашний студент-одиночка, пишущий весь код в main, лишь бы сработало".

IT>Под тесты подпадает лишь пункт 2. Но топик-стартер говорит об обычном бизнес приложении, которое массажирует какие-то там данные в БД и гоняет их туда сюда. Применение DI

А какие альтернативы? Статики? Синглтоны? Спасибо, наелся.

IT>в таком контексте даёт как раз те самые невменяемые и никому не нужные тесты.

По-моему опыту, обычно получаются что если тесты невменяемые ненужные, то это означает что они тестируют невменяемый ненужный код.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 14.11.14 21:05
Оценка:
Здравствуйте, vmpire, Вы писали:

V>>>Совсем не нужны IoC-и когда схема связи классов простая и статическая.

.>>А это почему? Как без DI писать вменяемые юнит-тесты?
V>Вы DI с mocks и stubs не путаете?
А как mocks и stubs подсовывать без DI? Через глобальные переменные, хаки байткода и прочий шлак?

V>И то и другое прекрасно реализуется без DI фреймворков. Да и не всегда нужны mocks и stubs, если тестируемая логика достаточно изолирована.

А вы IoC/DI с IoC-containers не путаете? Фреймворки, конечно, необязательны.
Достаточно изолирована, следует понимать "юнит", который тестируют. Нет зависимостей — хорошо, повезло, появятся — заинжектим.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 14.11.14 21:39
Оценка: +1 -1
Здравствуйте, ., Вы писали:

IT>>1. У тебя pluggable application.

.>Следует читать "легко переиспользуемый код".

Следует читать "расширяемое приложение, которое понятия не имеет что в нём будет выполняться".

IT>>2. Приложение использует внешние сервисы, которые желательно мокать и фейкать при тестировании.

.>Следует читать "зависит от других частей с нетривиальным поведением".

Следует читать как "зависит от других частей с недетерминированным поведением"

IT>>3. Ты пишешь книгу или пост в блог и хочешь выглядеть нереально крутым чуваком.

.>Следует читать "не вчерашний студент-одиночка, пишущий весь код в main, лишь бы сработало".

Следует читать "охота повыпендриваться и казаться умным перед девчонками". Девчонок можно заменить по вкусу.

IT>>Под тесты подпадает лишь пункт 2. Но топик-стартер говорит об обычном бизнес приложении, которое массажирует какие-то там данные в БД и гоняет их туда сюда. Применение DI

.>А какие альтернативы? Статики? Синглтоны? Спасибо, наелся.

Альтернатива прямо противоположная — чистая функция, принимающая всё необходимое в качестве параметра и возвращающая результат как возвращаемое значение, не создавая и не используя при этом никаких артефактов вроде статиков и вызовов с высоким уровнем косвенности.

IT>>в таком контексте даёт как раз те самые невменяемые и никому не нужные тесты.

.>По-моему опыту, обычно получаются что если тесты невменяемые ненужные, то это означает что они тестируют невменяемый ненужный код.

Именно. Топик-стартер предложил в качестве задачки трёхзвенку с BL, DAL и EF, но не понмает куда здесь воткнуть IoC. Поделись с ним своим опытом на его примере и расскажи куда тут качественно притулить IoC. Я тоже послушаю, хотя мне уже и так всё давно понятно, но всё равно поржу.
Если нам не помогут, то мы тоже никого не пощадим.
Re[8]: DDD, архитектура и т.д.
От: AndrewJD США  
Дата: 14.11.14 22:16
Оценка:
Здравствуйте, IT, Вы писали:

AJD>>Например, внутренний сервис который реализует сложные алгоритмические расчеты.


IT>Сложные или тормознутые?


И сложные и/или тормознутые. Или например есть компонт который расчитывает сложную аналитику, которому нужны куча данных но он возвращает простой ответ: "Да" или "Нет".
Вместо того чтобы пытаться восоздать сложное окружения для этого компонента в юнит тесте, не проще его замокать?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[6]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 14.11.14 23:39
Оценка: 10 (1) +1
Здравствуйте, IT, Вы писали:

IT>>>1. У тебя pluggable application.

.>>Следует читать "легко переиспользуемый код".
IT>Следует читать "расширяемое приложение, которое понятия не имеет что в нём будет выполняться".
Эээ. По тестам видно — что.

IT>>>2. Приложение использует внешние сервисы, которые желательно мокать и фейкать при тестировании.

.>>Следует читать "зависит от других частей с нетривиальным поведением".
IT>Следует читать как "зависит от других частей с недетерминированным поведением"
"недетерминированным во времени" — запросто, проект-то меняется.

IT>>>3. Ты пишешь книгу или пост в блог и хочешь выглядеть нереально крутым чуваком.

.>>Следует читать "не вчерашний студент-одиночка, пишущий весь код в main, лишь бы сработало".
IT>Следует читать "охота повыпендриваться и казаться умным перед девчонками". Девчонок можно заменить по вкусу.
А что такого выпедрючного в DI?

IT>>>Под тесты подпадает лишь пункт 2. Но топик-стартер говорит об обычном бизнес приложении, которое массажирует какие-то там данные в БД и гоняет их туда сюда. Применение DI

.>>А какие альтернативы? Статики? Синглтоны? Спасибо, наелся.
IT>Альтернатива прямо противоположная — чистая функция, принимающая всё необходимое в качестве параметра и возвращающая результат как возвращаемое значение, не создавая и не используя при этом никаких артефактов вроде статиков и вызовов с высоким уровнем косвенности.
Сколько будет аргументов у функции "деактивировать пользователя"?

IT>>>в таком контексте даёт как раз те самые невменяемые и никому не нужные тесты.

.>>По-моему опыту, обычно получаются что если тесты невменяемые ненужные, то это означает что они тестируют невменяемый ненужный код.
IT>Именно. Топик-стартер предложил в качестве задачки трёхзвенку с BL, DAL и EF, но не понмает куда здесь воткнуть IoC. Поделись с ним своим опытом на его примере и расскажи куда тут качественно притулить IoC. Я тоже послушаю, хотя мне уже и так всё давно понятно, но всё равно поржу.
Давай. Code talks, однако.
Смотрим требования:

И вот я представляю как мне нужно юзеру просто поставить IsActive в false.

Во первых, пересмотрим их с т.з. бизнеса. Такого требования "просто поставить IsActive в false" никакой вменяемый БА не выдаст. Скорее всего он скажет "хочу кнопку деактивации пользователя". Опустим для простоты UI, пишем серверную часть, т.е. начнём с слоя-представления. Он принимает просто ID, т.к. приходит по сети rest запрос.
class UserManager
{
  final UserRepo userRepo;
  final SomeSecurity someSecurity;
  final UserService userService;
  final SessionService sessionService;
  //ctor

  @аннотации, чтобы опубликовать этот метод как REST API, может ещё декларативное управление транзакциями, т.к. findById и update(user) должны выполнятся в одной транзакции.
  void deactivateUser(long userId)
  {
    User user = userRepo.findById(userId);
    if(user == null)
      throw new NotFoundException("boom");
    if(sessionService.getCurrentUser().equals(user) && !user.isMad())
      throw new SuicideNotAllowedException();
    someSecurity.checkSomething(user);
    userService.deactivateUser(user);
    // ещё обычно формируется ответ, но для простоты пусть будет void
  }
}

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

Дальше бизнес слой, который описывает что в общем-то надо делать.
class UserService
{
  final AuditSerivce auditSerivce;
  final UserRepo userRepo;
  final CommunicationService communicationService;
  //ctor

  void deactivateUser(User user)
  {
    if(!user.isActive()) return;

    auditSerivce.reportAction(AuditOperation.DEACTIVATE_USER, user.getId());

    user.setActive(false);
    userRepo.update(user);
    communicationService.sendMessage(user, "sorry, I don't like you");
  }
}

Здесь всего два теста — не деактивировать деактивированного, ну и assert/verify остальное — смена active флага, вызов сохранения, посылка сообщения и факт аудита.
class UserRepo
{
   final Database database;
   //ctor
   User findById(long id)
   {
     return database.selectQuery....;// запрос, скорее всего с какими-нибудь фильтрами, например, чтобы скрыть логически удалённых юзеров.
   }
   void update(User user)
   {
     database.updateQuery....;
   }
}

тут будут, скорее всего, только интеграционные тесты, которые проверяют, что по некой тестовой БД репозиторий возвращает правильно сконструированного пользователя из правильных полей, правильно отфильтрованного, и т.п. Сохраняет то, что должен сохранять и т.п. В принципе при использовании linq этот слой сильно упрощается, иногда вплоть до того, что его можно выкинуть, хотя скорее всего он выродится в переиспользуемые части запросов, которые ещё можно комбинировать по месту.

А вот собственно конфигурирование DI контекста приложения. Делаем вручную, без фреймворков:
class ApllicationContext
{
  void configure()
  {
    Database database = new Database("connectionString for data");
    Database auditDatabase = new Database("connectionString for audit");
    SmtpClient smtpClient = new SmtpClient("mail.google.com", 25);
    RestApiPublisher restApiPublisher = new RestApiPublisher("my.service.com", 80);

    UserRepo userRepo = new UserRepo(database);

    AuditSerivce auditSerivce = new AuditSerivce(auditDatabase);
    CommunicationService communicationService = new CommunicationService(smtpClient);
    UserService userService = new UserService(auditSerivce, userRepo, communicationService);
    SessionService sessionService = new SessionService(restApiPublisher);

    UserManager userManager = new UserManager(userRepo, someSecurity, userService, sessionService);

    restApiPublisher.publish("url/to/user/api", userManger);
    restApiPublisher.start();
  }
}

Тут логики никакой, никаких if, тупой последовательный код, юнит-тесты не нужны, но можно интеграционные.

Можешь начинать ржать, но во время ржания пожалуйста напиши мне аналогичный код в котором только "чистая функция, принимающая всё необходимое в качестве параметра и возвращающая результат как возвращаемое значение".
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 15.11.14 00:12
Оценка:
Здравствуйте, ., Вы писали:

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


V>>>>Совсем не нужны IoC-и когда схема связи классов простая и статическая.

.>>>А это почему? Как без DI писать вменяемые юнит-тесты?
V>>Вы DI с mocks и stubs не путаете?
.>А как mocks и stubs подсовывать без DI? Через глобальные переменные, хаки байткода и прочий шлак?
Через явную передачу интерфейсов, через конфигурационный файл, через специальные фреймворки типа moles...
Если вам лично это не нравится — это не значит, что это шлак.

V>>И то и другое прекрасно реализуется без DI фреймворков. Да и не всегда нужны mocks и stubs, если тестируемая логика достаточно изолирована.

.>А вы IoC/DI с IoC-containers не путаете? Фреймворки, конечно, необязательны.
.>Достаточно изолирована, следует понимать "юнит", который тестируют. Нет зависимостей — хорошо, повезло, появятся — заинжектим.
Не "повезло", а "разумно спроектировано"
Re: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.11.14 02:32
Оценка:
Здравствуйте, SharpDeveloper, Вы писали:

SD>И вот я представляю как мне нужно юзеру просто поставить IsActive в false.

SD>- Слой представления создаёт UserManager (разумеется ИоКи там всякие!) и вызывает метод бизнес слоя UserManager.SetActiveFlag(false)
SD>- Бизнес слой через Иоки создает репозиторий
SD>- Вызываем метод Update UserRepository, или даже UserRepository.SetActiveFlag
SD>- Репозиторий создает датаконтекст и do the trick....
"Правильные посоны" еще делают хранимку в СУБД, SetUserActive
А еще может быть ApplicationFacade, чтобы ... даже затрудняюсь сказать зачем. Но вдург мы захотим вместо WebUI сделать WebAPI и "чтобы не копипастить код" сделаем еще один уровень косвенности.


SD>Куда от этого бежать?!!?!?

Забить болт на все это барахло. Идиотов не слушать.

SD>Я не настаиваю на открытии коннекшена прямо в контроллере и выполнения SQL запроса предварительно подготовленного StringBuilder'ом.... но как то трехзвенка меня разочаровывает.

При чем здесь трехзвенка? Тут еще звеньями и не пахнет.
Re[7]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.11.14 02:34
Оценка: -1
Здравствуйте, AndrewJD, Вы писали:

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


AJD>>>А если приложение использует внутренний сложный сервис? Почему бы его тоже не мокать и фейкать при тестировании?


IT>>Например?


AJD>Например, внутренний сервис который реализует сложные алгоритмические расчеты.


Например? Найди в своем проекте такой сервис.
Re[9]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.11.14 02:43
Оценка: -1
Здравствуйте, AndrewJD, Вы писали:

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


AJD>>>Например, внутренний сервис который реализует сложные алгоритмические расчеты.


IT>>Сложные или тормознутые?


AJD>И сложные и/или тормознутые. Или например есть компонт который расчитывает сложную аналитику, которому нужны куча данных но он возвращает простой ответ: "Да" или "Нет".

AJD>Вместо того чтобы пытаться восоздать сложное окружения для этого компонента в юнит тесте, не проще его замокать?

Сложную аналитику на основе чего? Если это внешние данные, то об этом и было сказано. Если эти данные внутри приложения то зачем тестировать отдельно?

Предположим:
1) Есть веб-приложение, оно принимает много данных через загрузку csv файла
2) В контроллере делается два действия: парсинг файла, вызов сервиса.
3) Внимание вопрос: сколько тестов надо написать?
а) один, который в контроллер скармливает файл и получает результат
б) три один тестирует контроллер, как он вызывает сервисы, второй тестирует парсинг, третий тестирует сервис

ЗЫ. На деле тестов будет удвоенное количество, ибо нужен и положительный, и отрицательный тест.

На практике вариант А гораздо лучше. Он ловит больше ошибок, он более устойчив к рефакторингам, он банально требует меньше кода для тестов и приложения.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.