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[7]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.11.14 02:56
Оценка: 9 (2) +3
Здравствуйте, ., Вы писали:

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


.>...поискипано

.>Тут логики никакой, никаких if, тупой последовательный код, юнит-тесты не нужны, но можно интеграционные.
Ты серьезно считаешь что такой код надо писать? А если тебе понадобится добавить инфу кто и когда деактивировал юзера, придется все сверху до низу менять? Это такая шутка?

.>Можешь начинать ржать, но во время ржания пожалуйста напиши мне аналогичный код в котором только "чистая функция, принимающая всё необходимое в качестве параметра и возвращающая результат как возвращаемое значение".


ASP.NET MVC
[Authorize("Admin")]
class UserConteroller 
{
    MyDataContext ctx = new MyDataContex();

    [Action("Deactivate")]
    [HttpPost]
    async ActionResult DeactivatePost(int userId)
    {
        var user = ctx.Users.First(u => u.Id == userId);
        user.Actve = false;
        await ctx.SaveChagesAsync();
        return Redirect("Index");
    }
}


Не нужно ничего. От слова вообще.
Права разруливает фреймворк, транзакционность — EF. Сама логика тривиальна, покрывать юнит-тестами не за чем.
Re[17]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.11.14 12:22
Оценка: 31 (2) +2
Здравствуйте, ., Вы писали:

.>Меня удивляет, что многие тут приводят образцы кода, изначально не предназначенные для тестирования. Сразу пишут legacy code.


Типичная подмена понятий.
Тестировать можно любой детерминированный код — который выдает одинаковый результат для одного и того же набора параметров и окружения. Это верно как для ручного, так и для автоматического тестирования.
Ты говоришь о юнит-тестировании, которое само по себе ценность не несет, ценность в отсутствии ошибок в следствие тестирования.
Но эффективность юнит-тестирования очень низка — http://gandjustas.blogspot.ru/2013/07/code-review-vs-testing.html, ниже интеграционных и системных тестов. Более того есть гораздо более эффективные методы устранения ошибок, типа формальных review.
Фактически означает, что писать больше и более запутанного кода для того, чтобы его моно было покрыть юнит-тестами — бесполезное занятие.

Лучше написать три строки в которых очевидно нет ошибки, чем 15 и покрыть их тестами.
Re[3]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 23.11.14 18:03
Оценка: +2 -1 :)
Здравствуйте, koodeer, Вы писали:

K>> Я не знаю, как в .NET, а в Java EE никаких "контекстов" создавать не надо, я просто пишу

K>
@PersistenceContext
K>private EntityManager em;

K>>и в путь.

K>Когда будет создан этот контекст? При старте приложения, при первом обращении к em или ещё когда? Он будет создаваться каждый раз или один раз до завершения приложения?

K>Что произойдёт при выбросе исключения? Будет ли нормально закрыто соединение с БД?
Суть в том, что все эти вопросы абсолютно нерелевантны к UserService, это поведение описывается отдельно, при настройке контекста приложения. Мало того, что ответы на эти вопросы могут быть разные в зависимости от варианта использования — как минимум всегда есть два варианта — обычный код и unit test. В реальности их может быть сильно больше.
Так вот, DI позволяет значительно упростить такое разделение. Т.е. вместо
class UserService
{
  void deactivateUser()
  {
    try(EntityManager em = new EntityManager())
    {
       em.executeQuery(...);
    }
  }
}

надо писать
class UserService
{
  @PersistenceContext private EntityManager em;

  void deactivateUser()
  {
       em.executeQuery(...);
  }
}

Вот этот элементарный приём позволяет сильно всё упростить.

K>И много других вопросов можно задать.

K>Я полагаю, суть всего этого холивара как раз в этом. Можно что-то указать многословно, но при этом гарантированно обработать все возможные ситуации. А можно гораздо более кратко и понятно, но есть риск, что останется необработанной какая-то исключительная ситуация.
А можно кратко и понятно, обрабатывая исключительные ситуации.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
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[8]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 15.11.14 15:04
Оценка: +3
Здравствуйте, gandjustas, Вы писали:

G>ASP.NET MVC

G>
G>[Authorize("Admin")]
G>class UserConteroller 
G>{
G>    MyDataContext ctx = new MyDataContex();

G>    [Action("Deactivate")]
G>    [HttpPost]
G>    async ActionResult DeactivatePost(int userId)
G>    {
G>        var user = ctx.Users.First(u => u.Id == userId);
G>        user.Actve = false;
G>        await ctx.SaveChagesAsync();
G>        return Redirect("Index");
G>    }
G>} 
G>


G>Не нужно ничего. От слова вообще.

Что значит не нужно? У меня в коде были бизнес-требования.
Куда ты дел аудит, посылку сообщения, проверку на то, что юзер уже деакивирован? Куда потерялся факт, что есть две базы — для данных и аудита?
Как сформируется правильное описание ошибки, что юзер с таким id не найден?

G>Права разруливает фреймворк, транзакционность — EF.

Конечно, если у тебя всё вписывается в дефолтные юзкейсы фреймворка (погоди, а какого ешё фреймворка? Вроде без них хотели). А если не примитивные права, а кастомные проверки, например, юзер с открытым заказами не может быть деактивирован.

G>Сама логика тривиальна, покрывать юнит-тестами не за чем.

А как ты считаешь, проверить, что пять строк выполнились успешно надо юнит-тестом покрывать? Или так сойдёт?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
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, архитектура и т.д.
От: 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[10]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 15.11.14 18:20
Оценка: +2
Здравствуйте, gandjustas, Вы писали:

G>>>Не нужно ничего. От слова вообще.

.>>Что значит не нужно? У меня в коде были бизнес-требования.
G>У тебя в коде была нечитаемая мешанина,
Эээ. Ты правда считаешь "ctx.Users.Where(u => !u.Orders.Any(o => o.CloseDate == null)).First(u => u.Id == userId)" более читаемая? Что тут происходит? Какое бизнес-требование?
Я тоже умею писать код в одну строчку, но вроде мы не о write-only коде.
Давай напиши окончательный вариант аналогичный моему и сравним читабельность и устойчивость к изменениям. Классы, которые у меня не реализованы (SomeSecurity, SessionService, CommunicationService, AuditSerivce, Database), будем считать писались другой командой, у тебя только есть интерфейсы которые ты волен задать сам. Что должно быть:
* конкретные ошибки (не ололо 500, всё пропало) NotFoundException, SuicideNotAllowedException,
* вызов проверки безопасности someSecurity.checkSomething
* аудит auditSerivce.reportAction
* посылка сообщения sendMessage
* Плюс каким образом будут задаваться всякие опции (коннекты к базам, stmp порт, етс).

И никакой магии, когда какой-то очередной волшебный фреймворк тебе сам всё сделает.

G>очень неусточивая к изменениям.

К каким например? Тесты есть, менять нестрашно.

.>>Куда ты дел аудит, посылку сообщения, проверку на то, что юзер уже деакивирован? Куда потерялся факт, что есть две базы — для данных и аудита?

G>У ТС не было такого, это ты уже сам придумал.
Верно, для того, чтобы поменять одно поле в БД, слои не нужны, можно прямо в коде представления зафигачить апдейт базы. Как я понял вопрос топика — накой вообще все эти слои. Я дополнил требования, чтобы каждый слой имел смысл, выполняя свою роль.

.>>Как сформируется правильное описание ошибки, что юзер с таким id не найден?

G>Это не нужно, ибо чтобы вызвать post с id нужно этот id получить. Если же такого id не существует, то "ололопыщьпыщьвсепропало" достаточно.
Ты никогда не писал public API? Когда тебя забрасывают вопросами "а что это такое http 500?".

G>>>Права разруливает фреймворк, транзакционность — EF.

.>>Конечно, если у тебя всё вписывается в дефолтные юзкейсы фреймворка (погоди, а какого ешё фреймворка? Вроде без них хотели). А если не примитивные права, а кастомные проверки, например, юзер с открытым заказами не может быть деактивирован.
G>Тогда будет примерно так:
G>
G>ctx.Users.Where(u => !u.Orders.Any(o => o.CloseDate == null)).First(u => u.Id == userId);
G>

G>В остальном не изменится.
И это тоже будет в asp.net-обработчике?

G>Конечно сойдет. У меня стоимость ошибки минимальна, изменения выкатить легко, да еще 100500 раз поменяется прежде чем реально потребуется.

Ну счастливый. Бывают ситуации когда час простоя даёт чистый убыток несколько сотен тыс $. Плюс труднооценимая потеря репутации.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: DDD, архитектура и т.д.
От: Alexander Polyakov  
Дата: 29.11.14 14:29
Оценка: +1 :)
IT>Есть мнение, что от DI нереально прутся прежде всего те, кому довелось осознать "магию" косвенных вызовов, но так и застрявших на этом уровне. Другими словами, чтобы понять что такое DI нужно стать эльфом 48-го уровня, а чтобы начать осознавать вред наносимый этой хренью нужно достигнуть 60-го уровня.

IT>За такой код в 2014-м году нужно пальцы железной ленейкой отбивать.

Надменно произнес эльф 63 уровня.
И только эльфы, достигшие 77 уровня, молча про себя подумали, что этот код наиболее близок к правильному варианту.
Re[5]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 23.11.14 19:17
Оценка: 2 (1)
Здравствуйте, koodeer, Вы писали:

.>>А можно кратко и понятно, обрабатывая исключительные ситуации.

K>Тогда почему выше по топику ты пишешь длинно и непонятно? Оппоненты как раз и приводят код в одну строчку, а весь остальной контекст передаётся через атрибуты (аннотации).
Потому что оппоненты приводят код, в котором на порядок меньше фич, и этот код эквивалентен по фичам одному однострочному методу моего кода
Автор: .
Дата: 16.11.14
, и к тому же мой код проще, т.к. не зависит от фреймворков и легче тестируется, т.к. использует DI. Они вот без moles уже не могут ничего сделать, mysql уже помянули. И это для тестирования "кода в одну строчку".
Сколько раз я просил показать как именно "остальной контекст передаётся через атрибуты", так никто и не смогзахотел. Я уверен, что эквивалетный по фичам код с атрибутами код будет только хуже, но оппоненты не хотят признаться.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 23.11.2014 19:18 · . Предыдущая версия .
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[3]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 14.11.14 18:47
Оценка: +1
Здравствуйте, ., Вы писали:


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

.>А это почему? Как без DI писать вменяемые юнит-тесты?
Вы DI с mocks и stubs не путаете?
И то и другое прекрасно реализуется без DI фреймворков. Да и не всегда нужны mocks и stubs, если тестируемая логика достаточно изолирована.
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) Внимание вопрос: сколько тестов надо написать?
а) один, который в контроллер скармливает файл и получает результат
б) три один тестирует контроллер, как он вызывает сервисы, второй тестирует парсинг, третий тестирует сервис

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

На практике вариант А гораздо лучше. Он ловит больше ошибок, он более устойчив к рефакторингам, он банально требует меньше кода для тестов и приложения.
Re[12]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 15.11.14 20:14
Оценка: +1
.>>Эээ. Ты правда считаешь "ctx.Users.Where(u => !u.Orders.Any(o => o.CloseDate == null)).First(u => u.Id == userId)" более читаемая? Что тут происходит? Какое бизнес-требование?
.>>Я тоже умею писать код в одну строчку, но вроде мы не о write-only коде.
G>Ну разбей на несколько строк, суть не изменится.
Каким образом разбить? Как распределить по коду? Как назвать куски? Это и есть вопрос слоёв и архитектуры.
Можно написать
int f(int a, b, c)
{
   return a + b - c;
}

а можно написать
int calculateAmountToCharge(int orderPrice, deliveryPrice, discount)
{
  int totalPrice = orderPrice + deliveryPrice;
  return totalPrice - discount;
}


.>>Давай напиши окончательный вариант аналогичный моему и сравним читабельность и устойчивость к изменениям. Классы, которые у меня не реализованы (SomeSecurity, SessionService, CommunicationService, AuditSerivce, Database), будем считать писались другой командой, у тебя только есть интерфейсы которые ты волен задать сам. Что должно быть:


.>>* конкретные ошибки (не ололо 500, всё пропало) NotFoundException, SuicideNotAllowedException,

G>Это неверный подход. Надо не позволять пользователю выполнить такие операции, скрывать\деактивировать кнопки на UI. А если же какойнить хакер сделает такой вызов, то "все пропало" достаточно.
Я говорю о UI разработчиках, пользователях API.

.>>* вызов проверки безопасности someSecurity.checkSomething

G>Так вообще писать нельзя. Проверки ролей безопасности должны быть в фильрах (атрибутах), а проверки данных в запросе.
А фильтры|атрибуты как работают? Магически опять? Или нужно кучу кода?
Да и вообще, фильтры|атрибуты или просто вызов метода — не принципиально, детали реализации. Суть архитектуры остаётся той же.

.>>* аудит auditSerivce.reportAction

.>>* посылка сообщения sendMessage
G>Зачем? это ты уже сам придумал.
Да, придумал. Допустим надо. Сложно это тебе добавить в код? Ведь он так гибок к изменениям. Главное на тестах съэкономить.

.>>* Плюс каким образом будут задаваться всякие опции (коннекты к базам, stmp порт, етс).

G>web.config
Допустим. А как оно в код попадёт? Заинжекится фреймворком?

.>>И никакой магии, когда какой-то очередной волшебный фреймворк тебе сам всё сделает.

G>В смысле? Не пользоваться тем, что есть и фигачить свое? Это же прямой путь к говнокоду.
Я не предлагаю не пользоваться фреймворками, а как планировать архитектуру. Понятно с .net, там потратили несколько человеколет, выдали фреймворк, где достаточно написать одну функцию, и всё работает. Суслика видишь?

G>>>очень неусточивая к изменениям.

.>>К каким например? Тесты есть, менять нестрашно.
G>Добавь дополнительный параметр в метод деактивации. окажется что надо сделать изменение на каждом слое, что в свою очередь вызовет правку сервисов каждого слоя.
G>Нуегонафиг.
Опять... "дополнительный параметр", а бизнес-требования то какие?
Да и в конце-концов... невелика беда. Добавление нового параметра в 10 функций займёт одну минуту.

.>>>>Куда ты дел аудит, посылку сообщения, проверку на то, что юзер уже деакивирован? Куда потерялся факт, что есть две базы — для данных и аудита?

G>>>У ТС не было такого, это ты уже сам придумал.
.>>Верно, для того, чтобы поменять одно поле в БД, слои не нужны, можно прямо в коде представления зафигачить апдейт базы. Как я понял вопрос топика — накой вообще все эти слои. Я дополнил требования, чтобы каждый слой имел смысл, выполняя свою роль.
G>Нет, ты придумал сложности там где их не было. Собственно так и происходит чаще всего в приложении. Приудмываются сложности, потом героически решаются. А надо было всего-то user.Active = false сделать.
Ок.
  void deactivateUser(long userId)
  {
     jdbcOperations.update("update user set active=false where id=?", userId);
  }

одна строчка. asp.net фтопку.

.>>>>Как сформируется правильное описание ошибки, что юзер с таким id не найден?

G>>>Это не нужно, ибо чтобы вызвать post с id нужно этот id получить. Если же такого id не существует, то "ололопыщьпыщьвсепропало" достаточно.
.>>Ты никогда не писал public API? Когда тебя забрасывают вопросами "а что это такое http 500?".
G>Для web api будет так:
G>
G>var user = await ctx.Users.FirstOrDefaultAsync(u => u.Id == userId);
G>if(user == null) return NotFound();
G>user.Active = false;
G>await ctx.SaveChangesAsync();
G>return Ok();
G>

Уже лучше, осталось остальное реализовать и понять, что кода у тебя будет не меньше для той же функциональности.

.>>И это тоже будет в asp.net-обработчике?

G>Если используется в нескольоких местах, то делается extension-метод:
G>
G>public static IQueryable<User> WithoutOpenOrders(this IQueryable<User> source)
G>{
G>    return source.Where(u => !u.Orders.Any(o => o.CloseDate == null)); 
G>}
G>

G>Далее код пишется так:
G>
G>ctx.Users.WithoutOpenOrders().First(u => u.Id == userId);
G>

G>Получается еще и DSL, что прекрасно.
Согласен. Можно и так. Осталось это объединить в одном namespace и внезапно получится UserDao.

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

G>Это не тот случай изначально.
Раз в этом формуе, то хочется верить в лучшее, что человек хочет что-то новое изучить, чтобы мог пойти работать в такую компанию. А так пусть пишет на PHP, и никаких забот.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: DDD, архитектура и т.д.
От: diez_p  
Дата: 17.11.14 13:36
Оценка: -1
Здравствуйте, gandjustas, Вы писали:

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


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


.>>...поискипано

.>>Тут логики никакой, никаких if, тупой последовательный код, юнит-тесты не нужны, но можно интеграционные.
G>Ты серьезно считаешь что такой код надо писать? А если тебе понадобится добавить инфу кто и когда деактивировал юзера, придется все сверху до низу менять? Это такая шутка?

.>>Можешь начинать ржать, но во время ржания пожалуйста напиши мне аналогичный код в котором только "чистая функция, принимающая всё необходимое в качестве параметра и возвращающая результат как возвращаемое значение".


G>ASP.NET MVC

G>
G>[Authorize("Admin")]
G>class UserConteroller 
G>{
G>    MyDataContext ctx = new MyDataContex();

G>    [Action("Deactivate")]
G>    [HttpPost]
G>    async ActionResult DeactivatePost(int userId)
G>    {
G>        var user = ctx.Users.First(u => u.Id == userId);
G>        user.Actve = false;
G>        await ctx.SaveChagesAsync();
G>        return Redirect("Index");
G>    }
G>} 
G>


G>Не нужно ничего. От слова вообще.

G>Права разруливает фреймворк, транзакционность — EF. Сама логика тривиальна, покрывать юнит-тестами не за чем.
Для гомогенного шуточного проекта пойдет.
Для системы все может быть сильно сложнее
приведенный код напоминает нуб-дельфи-стайл
OnButtonClick { /* поехали писать в базу, пусть даже и в другом потоке*/ }

Код точки нравится больше. Если мне надо лезть в работу с пользователями, для этого есть UserManager, который знает или делегирует знания кому надо.
Отредактировано 17.11.2014 14:55 diez_p . Предыдущая версия .
Re[9]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 17.11.14 16:21
Оценка: +1
Здравствуйте, ., Вы писали:

IT>>Т.е. у нас проблемы с пониманием что такое расширяемое приложение?

.>Конечно, под этим понимать можно всё что угодно. Типичный баззворд.

Т.е. у нас ещё и проблемы с пониманием что такое баззворд.

IT>>Это как раз самое простое. Сложнее когда тест зависит от внешних компонент, которые, например, могут быть временно недоступны.

.>Это будет интеграционный тест. Что вообще параллельно юнит-тесту.

А ты в курсе какие тесты имел ввиду топик-стартер?

.>Как я понял из вышенаписанного — достичь 60-го уровня можно только если есть какой-нибудь фреймворк типа ASP.NET, и заявлять что DI и фреймворки хрень, игнорируя факт, что этот самый фреймворк делает этот самый DI.


Не понял мысли.

.>В моей реализации получилось один — user. Интересно сколько будет в твоей.


В моей нет ни одного юзера, есть только UserID.

.>Аналог твоего кода ещё примитивнее:

.>
.>void deactivateUser(long userId)
.>{
.>  jdbcOperations.update("update user set active=false where id=?", userId);
.>}
.>


За такой код в 2014-м году нужно пальцы железной ленейкой отбивать.

.>Даже без всяких фреймворков. И что? Я прошу код аналогичный моему по функциональности, с посылкой сообщений, аудитом, различными проверками, обработкой ошибок и т.п.


Аудита в твоём коде не обнаружено. Если различные проверки — это checkSomething, то желательно раскрыть свою мысль. А с твоим подходом к обработке ошибок я не согласен.
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 18.11.14 19:57
Оценка: -1
Здравствуйте, gandjustas, Вы писали:

G>Не в аудит добавить, а в юзера.

Как это в юзера? Зачем?? Но в общем не важно, это не конец света, т.к. есть несколько способов, выбирай по обстоятельствам.
Между слоями инфу можно передавать несколькими путями:
1. Между соседними слоями обычно просто через параметры методов.
2. Между далеко разрозненными слоями — через контекст.
3. В некоторых случаях когда параметров жутко много (больше ~десяти), то создают объект-структуру для этих параметров, тогда добавление нового поля будет тривиально.

G>Аудит вообще не нужен.

Как не нужен? Повезло, если тебе не нужен. Если в некоторых проектах не будет правильного организованного аудита, то в лучшем случае оштрафуют на несколько сотен миллионов долларов, а то и вообще лавочку прикроют.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[22]: DDD, архитектура и т.д.
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.11.14 11:58
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

.>>Не понял я. В смысле добавить параметр в deactivateUser? Напугал ежа. Да пожалуйста, одна строчка добавится, ну и один тест...

G>Вранье, у тебя три "слоя" и, соответственно, три передачи параметров. Это значит как минимум в трех местах поменять.
Believe me, там ещё наверняка внизу хранимые процедуры (четыре штуки), и в каждой надо не забыть его добавить и поправить тело.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[25]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.11.14 13:52
Оценка: +1
Здравствуйте, ., Вы писали:

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


.>Коду я верю, т.к. его можно компилятором проверить, "бла-бла-бла" — не верю, особенно когда никаких доказательств нет.

Ты и сам привел не компилируемый код для начала


G>>Правильно, поэтому упрощай код любыми доступными средствами.

.>"Всё следует упрощать до тех пор, пока это возможно, но не более того."!
Угу, ты этомму правилу не следуешь.

G>>>>И про атрибуты объяснил. Но ты до сих пор не веришь, что можно написать просто.

.>>>Не надо мне объяснений. Код покажи, сотый раз прошу. Пока нет кода — не поверю.
G>>Я показал уже, ты не веришь. Зачем еще что-то писать?
.>Ты не показал ничего, что не было в моём коде, всё то же самое. Зато не показал многого из того, что было в моём коде.
А в твоем коде ничего "больше" не было. Пара вызовов методов реализация которых неизвестна.

.>Атрибуты это те же слои, только выражены другими языковыми конструкциями. Со своими достоинствами и недостатками.

Не те же. Атрибуты ортогональны.

G>>Это вранье, классы получают ссылки на другие классы, которые в общем случае не stateless. Ну или ты не узнаешь об этом пока все не прочитаешь. Состояние транзитивно. Достаточно в одном классе поиметь состояние, кк все зависящие от него также получают это состояние.

.>Т.к. зависимостями ты управляешь сам, ты сам контролируешь и состояние. Классы сами по себе stateless.
Ты забыл про внешние зависимости. Внезапно большинство ORM отдают stateful классы контекста, которые у тебя прямо или косвенно заинжектены во все сервисы.

G>>А я тебе привел, один метод, этого более чем дотаточно. Нужно будет аудит — накручивается через атрибуты.

G>>http://rionscode.wordpress.com/2013/03/03/implementing-audit-trails-using-asp-net-mvc-actionfilters/
G>>Вот пример как аудит атрибутами накрутить.
.>Ок. Допустим ты это добавил (кстати код явно не проще моего).
.>Давай теперь проверим твою любимую поддатливость к изменениям. Появилось требование в аудит записать userId. Потом ещё одно: записать аудит только если активный юзер деактивируется.
Тоже через атрибуты\AOP решается, но навешивается на контекст, а не на контроллер.

.>Он не делает всё, что требуется. Список БА-требований тут: http://rsdn.ru/forum/design/5867830?tree=tree
Автор: .
Дата: 24.11.14

Послать нафиг БА и решать реальную проблему, а не надуманную.

.>Мы же вроде к простоте стремимся, а тут ВНЕЗАПНО выясняется, что к трём строкам добавляется ещё "гораздо больше". Но мы это не считаем, потому что не хотим.

Ты и сам этот код не привел

G>>Ты же не привел реализацию аудита и проверок. Я с тем же успехом просто добавил атрибут на метод. Я тебе уже приводил этот код:

.>Реализацию аудита я приводил
Автор: .
Дата: 17.11.14
— 3 строки. Проверки, пусть будет 3rd party RPC.


.>Не эквивалетнен. Где проверки, где идемпотентность (деактивация деактивированного)?

Сам догадаешься куда if добавить?

.>Где посылка сообщения?

Атрибут добавишь или помочь? Или добавь одну строку в метод, в принципе без разницы.

.>Обаботака ошибочных сиутаций?

В атрибуте Не догадался еще как хорошая архитектура выглядит?

.>В общем пройдись по списку требований и покажи как что реализовано в твоём коде.

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

.>Тем, что это сложнее и становится оправданным только для весьма ограниченного числа применений.

На практике не сложнее и подходит гораздо чаще, чем ты думаешь. Просто в javaне принято делать атрибуты, а в .NET это используется постоянно.

.>То что вызывается в нужный момент (скажем, аудит вызывается до|после SomeChecks), что вызывается с нужными аргументами, что правильно реагирует на исключения, етс.

Сделай интеграционный тест. Ты можешь из кода вызвать не просто весь пайплайн asp.net MVC, а можно даже сделать веб-вызов, который отработает все блоки.
http://blog.nwoolls.com/2013/05/30/integration-testing-asp-net-mvc-projects-with-specsfor-mvc/
Один такой тест сделает ненужным около 20 юнит-тестов. Причем 20 юнит тестов будут отлавливать меньше ошибок, чем один такой тест.


.>>>>>Атрибут лишь хорош, если тебя в большинстве методов надо делать ровно одно и то же.

G>>>>Так и есть. Мы ведь говорили об аудите, это разве не надо делать в большинстве методов?
.>>>Не путаем аудит с логгированием. Нет, не надо во всех, а только в тех методах, которые делают business sensitive operations.
G>>То есть в 90%, какая разница?
.>Нет, даже в нашем deactivateUser он либо вызывается, либо не вызывается. 50% получается.
Так ты определись это аудит или лог. Аудит для действия пользователя, лог для поведения приложения. Соотвественно аудит пишется всегда, его надо в атрибуты, а лог может зависеть от условий — его методами. Но для лога есть отличное решение в .NET — ETW называется.

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

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

G>>>>А тебе надо тестировать наличие атрибута? Совсем двинулся? Посмотреть нельзя?

.>>>Как протестить что аудит случается только если активный юзер деактивируется? Т.е. деактивация деактивированного юзера не создаёт запись аудита.
G>>Просто не показывать UI пользователю с возможностью деактивировать уже деактивированного юзера. Ты не на том уровне пытаешься проблему решать.
G>>Ты понимаешь, что такая логика, как у тебя, в реальном приложении приведет к тонне говнокода?
.>Причём тут UI?! Что такое idempotent operation в контексте web приложения знаешь? Да и который UI? Их может быть куча разных, написанных/переписанных разыми командами|организациями.
А при чем тут идемпотентность? Присваивание значения полю — самая идемпотентная операция, даже никакой логики не нужно.

G>>>>>>В том, что меньше связей.

.>>>>>Код в студию, или не было.
G>>>>Код чего? Ты сам class coupling не можешь померить?
.>>>Ты говоришь "меньше". Меньше чем что? Померить в твоём воображаемом коде? Нет, извини, не могу.
G>>Я выше код привел — измеряй.
.>Там реализованно меньше половины требований, бОльшая и самая интересная часть скрыта за атрибутами. Давай всё показывай, не стесняйся.

G>>См выше, полностью эквивалентный по фичам — вызывается два метода, которые неизвестно что делают.

.>Ну добавь теперь туда колонку, которая что-то в аудит пишет. В скольких местах придётся изменять? Мне кажется, что не в меньшем количестве мест, чем в моём коде.
Тебе кажется.

G>>Забить на юнит-тесты.

.>Жуть.
В случае примитивной логики юнит-тесты только мешают. Лучше интеграционные тесты делать.

G>>Кстати зачем тебе юнит-тест, который багу не ловит, если у тебя есть интеграционный тест, который ловит?

.>Юнит-тест выполняется на несколько порядков быстрее интеграционного.
И что? Если он не ловит баги, то без разницы как быстро он выполняется.

.>Юнит-тесты могут выполнять на порядок больше проверок, всяких corner case, необычных use case, т.е. самих тестов может быть на порядки больше.

Это хорошо? Как раз наоборот, программисты начинают гоняться за покрытием неважных\несуществующих сценариев, вместо важных.
Например та же деактивация пользователя. Ты будешь все сценарии покрывать тестами, особенно много у тебя получится для всяких ошибок — польователь не найден, пользователь уже деактивирован итп.
А по факту на UI выводятся только существующие пользователи и кнопка "деактивировать" есть только у активных пользователей. То есть в нормальной работе ни один из протестированных тобой сценариев не будет вызываться.

.>Юнит-тест нужен для девелопмента. Интеграционный — для QA.

Тебе лично чем юнит тесты помогают? Ловят ошибки, которые ты не совершил бы, если бы не пытался покрыть код юнит-тестами?

.>>>Показывает _ожидаемое_ поведение. Скажем, типичный HttpRequest может быть иметь 100500 различных состояний, там и куча параметров, атрибутов, куки, сессии, разные версии протокола. Это не означает, что тесты невозможны. Просто твой код будет реагировать на некое вполне определённое подмножество состояний, которые легко описывать в виде моков.

G>>Ага,а по факту окажется, что в программе совершенно другой набор состояний и тест твой по сути не проверяет ничего.
.>Делов-то, тест гвоздями не прибит. Поправлю тест, чтобы он имел идентичный фактическому набор состояний, тут же увижу как мой код стал на это дело реагировать.
А как ты узнаешь фактический набор состояний?
А зачем вообще писать тест, когда ты не знаешь фактический набор состояний?

.>>>unit-тест будет не сложнее тестируемого кода.

G>>Это зависит от кода, если логика примитивна (поменять поле в базе), то юнит-тест окажется сложнее кода. Просто потому что setup будет занимать примерно половину объема теста.
.>Если писать код в стиле твоих любимых статиков "WithoutOrders", то да, может и окажется. Но тут всё просто — не надо писать так.
Как раз WithoutOrders прекрасно тестируется, ибо чистая функция.А как ты предлагаешь писать?
У меня есть прекрасный пример, когда люди думали что "не надо писать так". Нафигаличи репозиториев с кучей методов под каждый запрос. Репозитории мокали в тестах и в тестах все прекрасно работало. А вот сами репозитории содержали тучи ошибок в запросах, ибо методы репозиториев размножались копипастой, где-то была просто кривая обработка параметров, поэтому в одних случаях работало прекрасно, в других выдавало неверные данные, а в третьих получался неверных запрос.
В итоге порототип с двумя контроллерами, 10 экшенами и 500 юнит-тестами при выкатывании на staging грохнулась с таким шумом, что чуть не уволили тимлида.

.>>>>>Куда потребуется, туда и добавлю, может лучше в securityService подойдёт, я не понимаю точно твоё требование.

G>>>>Да все ты понимаешь, и понимаешь, что такой простой кейс начинает проникать в кучу классов, увеличивая сложность поддержки.
.>>>Увеличивая по сравнении с чем?
G>>По сравнению с АОП
.>Вроде у нас была простая задача, но ВНЕЗАПНО выяснилось, что оказывается даже в простые задачи нужно пихать АОП, и мы жить не можем без "реализация атрибутов гораздо больше, чем три строки". Круто, чё.
Так ты сам простую задачу усложнил до состояния, что стал нужен АОП. Это твое достижение, не нужно делиться лаврами.
И самое хреновое, что такое и в реальных проектах встречается чуть менее, чем всегда.

.>А class coupling снижается точно так же не только введением атрибута, но и введением интерфейса. Разницы никакой абсолютно. И ещё... Сюрприз! Но аннотации (то что в шарпе назывется атрибутами) в языке Java определяются с помощью ключевого слова "interface".

А ты считал? Я вот считал. Class coupling это грубо говоря количество ссылок между классами. Атрибуты вообще убирают ссылки, а интерфейсы — нет. Так что разница есть и весьма заметная.

.>>>Честно говоря, в начальном вопросе мне не удаётся усмотреть необходимость применения AOP.

G>>А его там и нет, это ты придумал сложности с BL\DAO и AOP стал оправданным.
.>В том виде, в котором ты его пытаешься оправдать он как-то плохо вписывается.
Это лишь твое мнение, которые ты не можешь обосновать никакими объективными факторами.

.>>>>>И что делать?

G>>>>Побить на маленькие.
.>>>Пример в студию. Особенно интересно как навешивать атрибуты на чистые статические функции.
G>>Это ты придумал фукнцию с десятком аргументов. Да и вообще все сложности, которые тут обсуждаем. Так что ты приводи пример, а потом будем думать как его решать.
.>Я привёл список бизнес-требований, привёл код. Твоя очередь.
У меня никогда не получится функции с 10+ аргументами при любых бизнес-требованиях. У тебя получается — тогда приводи пример, будем разбираться.

.>>>>>Хороший дев перерисовывает это один-к-одному в виде BL.

G>>>>Тогда откуда берутся 100500 сервисов, как ты в примере написал? Или ты не хороший дев?
.>>>UserService и есть BL. Но помимо BL есть и другие слои.
G>>Я боюсь представить что изобразил BA, что "один-к-одному в виде BL" получился userservice.
.>"нужна операция деактивации пользователя со следующими требованиями:..." и т.д. по тому списку.
И? ты самое важное в итоге пропустил
Но даже при этом:
1) Что такое операция, почему она у тебя превратилась в метод веб-сервиса?
2) Откуда у тебя взялись слои, о них в требованиях написано?
Re[28]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 16:03
Оценка: -1
Здравствуйте, Yoriсk, Вы писали:

.>>Непонятные слова типа "идемпотентность" твоим мозгом просто игнорируются?


Y>Хм, судя по вашему сообщению оно непонятно скорее вам, чем мне. Если вам и вправду нужна провернка на идемпотентность для кода

Y>
Y>user.Actve = false
Y>


Y>то это лучше как-то внешними тестами, тело метода-то тут при чём?

Я уже писал что к чему, повторяться я не хочу. Читай мои сообщения, там описано что именно делает эта проверка.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 29.11.14 19:47
Оценка: :)
Здравствуйте, Alexander Polyakov, Вы писали:

IT>>За такой код в 2014-м году нужно пальцы железной ленейкой отбивать.

AP>Надменно произнес эльф 63 уровня.

Ещё можно отрезать что-нибудь не нужное.

AP>И только эльфы, достигшие 77 уровня, молча про себя подумали, что этот код наиболее близок к правильному варианту.


С такими думами 77-й уровень можно только купить. А pay-to-win неспортивно и неинтересно.
Если нам не помогут, то мы тоже никого не пощадим.
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[2]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 14.11.14 18:18
Оценка:
Здравствуйте, vmpire, Вы писали:

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

А это почему? Как без DI писать вменяемые юнит-тесты?
Ок, если это прототип, то может и можно обойтись без юнит-тестов, но реальный проект... куда сейчас без этого?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
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:05
Оценка:
Здравствуйте, vmpire, Вы писали:

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

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

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

А вы IoC/DI с IoC-containers не путаете? Фреймворки, конечно, необязательны.
Достаточно изолирована, следует понимать "юнит", который тестируют. Нет зависимостей — хорошо, повезло, появятся — заинжектим.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
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[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[6]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 15.11.14 08:40
Оценка:
Здравствуйте, vmpire, Вы писали:

V>>>Вы DI с mocks и stubs не путаете?

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

V>через конфигурационный файл,

Это как? Очередной xml?

V>через специальные фреймворки типа moles...

Как он работает? Как переопределять статики?

V>Если вам лично это не нравится — это не значит, что это шлак.

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

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

.>>А вы IoC/DI с IoC-containers не путаете? Фреймворки, конечно, необязательны.
.>>Достаточно изолирована, следует понимать "юнит", который тестируют. Нет зависимостей — хорошо, повезло, появятся — заинжектим.
V>Не "повезло", а "разумно спроектировано"
Code talks
Автор: .
Дата: 15.11.14
, покажи как спроектировать разумно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.11.14 17:24
Оценка:
Здравствуйте, ., Вы писали:

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


G>>ASP.NET MVC

G>>
G>>[Authorize("Admin")]
G>>class UserConteroller 
G>>{
G>>    MyDataContext ctx = new MyDataContex();

G>>    [Action("Deactivate")]
G>>    [HttpPost]
G>>    async ActionResult DeactivatePost(int userId)
G>>    {
G>>        var user = ctx.Users.First(u => u.Id == userId);
G>>        user.Actve = false;
G>>        await ctx.SaveChagesAsync();
G>>        return Redirect("Index");
G>>    }
G>>} 
G>>


G>>Не нужно ничего. От слова вообще.

.>Что значит не нужно? У меня в коде были бизнес-требования.
У тебя в коде была нечитаемая мешанина, очень неусточивая к изменениям.

.>Куда ты дел аудит, посылку сообщения, проверку на то, что юзер уже деакивирован? Куда потерялся факт, что есть две базы — для данных и аудита?

У ТС не было такого, это ты уже сам придумал.

.>Как сформируется правильное описание ошибки, что юзер с таким id не найден?

Это не нужно, ибо чтобы вызвать post с id нужно этот id получить. Если же такого id не существует, то "ололопыщьпыщьвсепропало" достаточно.

G>>Права разруливает фреймворк, транзакционность — EF.

.>Конечно, если у тебя всё вписывается в дефолтные юзкейсы фреймворка (погоди, а какого ешё фреймворка? Вроде без них хотели). А если не примитивные права, а кастомные проверки, например, юзер с открытым заказами не может быть деактивирован.
Тогда будет примерно так:
ctx.Users.Where(u => !u.Orders.Any(o => o.CloseDate == null)).First(u => u.Id == userId);

В остальном не изменится.

G>>Сама логика тривиальна, покрывать юнит-тестами не за чем.

.>А как ты считаешь, проверить, что пять строк выполнились успешно надо юнит-тестом покрывать? Или так сойдёт?
Конечно сойдет. У меня стоимость ошибки минимальна, изменения выкатить легко, да еще 100500 раз поменяется прежде чем реально потребуется.
Re[11]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.11.14 18:46
Оценка:
Здравствуйте, ., Вы писали:

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


G>>>>Не нужно ничего. От слова вообще.

.>>>Что значит не нужно? У меня в коде были бизнес-требования.
G>>У тебя в коде была нечитаемая мешанина,
.>Эээ. Ты правда считаешь "ctx.Users.Where(u => !u.Orders.Any(o => o.CloseDate == null)).First(u => u.Id == userId)" более читаемая? Что тут происходит? Какое бизнес-требование?
.>Я тоже умею писать код в одну строчку, но вроде мы не о write-only коде.
Ну разбей на несколько строк, суть не изменится.

.>Давай напиши окончательный вариант аналогичный моему и сравним читабельность и устойчивость к изменениям. Классы, которые у меня не реализованы (SomeSecurity, SessionService, CommunicationService, AuditSerivce, Database), будем считать писались другой командой, у тебя только есть интерфейсы которые ты волен задать сам. Что должно быть:


.>* конкретные ошибки (не ололо 500, всё пропало) NotFoundException, SuicideNotAllowedException,

Это неверный подход. Надо не позволять пользователю выполнить такие операции, скрывать\деактивировать кнопки на UI. А если же какойнить хакер сделает такой вызов, то "все пропало" достаточно.

.>* вызов проверки безопасности someSecurity.checkSomething

Так вообще писать нельзя. Проверки ролей безопасности должны быть в фильрах (атрибутах), а проверки данных в запросе.

.>* аудит auditSerivce.reportAction

.>* посылка сообщения sendMessage
Зачем? это ты уже сам придумал.

.>* Плюс каким образом будут задаваться всякие опции (коннекты к базам, stmp порт, етс).

web.config

.>И никакой магии, когда какой-то очередной волшебный фреймворк тебе сам всё сделает.

В смысле? Не пользоваться тем, что есть и фигачить свое? Это же прямой путь к говнокоду.

G>>очень неусточивая к изменениям.

.>К каким например? Тесты есть, менять нестрашно.
Добавь дополнительный параметр в метод деактивации. окажется что надо сделать изменение на каждом слое, что в свою очередь вызовет правку сервисов каждого слоя.
Нуегонафиг.

.>>>Куда ты дел аудит, посылку сообщения, проверку на то, что юзер уже деакивирован? Куда потерялся факт, что есть две базы — для данных и аудита?

G>>У ТС не было такого, это ты уже сам придумал.
.>Верно, для того, чтобы поменять одно поле в БД, слои не нужны, можно прямо в коде представления зафигачить апдейт базы. Как я понял вопрос топика — накой вообще все эти слои. Я дополнил требования, чтобы каждый слой имел смысл, выполняя свою роль.
Нет, ты придумал сложности там где их не было. Собственно так и происходит чаще всего в приложении. Приудмываются сложности, потом героически решаются. А надо было всего-то user.Active = false сделать.


.>>>Как сформируется правильное описание ошибки, что юзер с таким id не найден?

G>>Это не нужно, ибо чтобы вызвать post с id нужно этот id получить. Если же такого id не существует, то "ололопыщьпыщьвсепропало" достаточно.
.>Ты никогда не писал public API? Когда тебя забрасывают вопросами "а что это такое http 500?".
Для web api будет так:
var user = await ctx.Users.FirstOrDefaultAsync(u => u.Id == userId);
if(user == null) return NotFound();
user.Active = false;
await ctx.SaveChangesAsync();
return Ok();


G>>>>Права разруливает фреймворк, транзакционность — EF.

.>>>Конечно, если у тебя всё вписывается в дефолтные юзкейсы фреймворка (погоди, а какого ешё фреймворка? Вроде без них хотели). А если не примитивные права, а кастомные проверки, например, юзер с открытым заказами не может быть деактивирован.
G>>Тогда будет примерно так:
G>>
G>>ctx.Users.Where(u => !u.Orders.Any(o => o.CloseDate == null)).First(u => u.Id == userId);
G>>

G>>В остальном не изменится.
.>И это тоже будет в asp.net-обработчике?
Если используется в нескольоких местах, то делается extension-метод:
public static IQueryable<User> WithoutOpenOrders(this IQueryable<User> source)
{
    return source.Where(u => !u.Orders.Any(o => o.CloseDate == null)); 
}

Далее код пишется так:
ctx.Users.WithoutOpenOrders().First(u => u.Id == userId);

Получается еще и DSL, что прекрасно.

G>>Конечно сойдет. У меня стоимость ошибки минимальна, изменения выкатить легко, да еще 100500 раз поменяется прежде чем реально потребуется.

.>Ну счастливый. Бывают ситуации когда час простоя даёт чистый убыток несколько сотен тыс $. Плюс труднооценимая потеря репутации.
Это не тот случай изначально.
Re[13]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.11.14 22:55
Оценка:
Здравствуйте, ., Вы писали:

.>>>Эээ. Ты правда считаешь "ctx.Users.Where(u => !u.Orders.Any(o => o.CloseDate == null)).First(u => u.Id == userId)" более читаемая? Что тут происходит? Какое бизнес-требование?

.>>>Я тоже умею писать код в одну строчку, но вроде мы не о write-only коде.
G>>Ну разбей на несколько строк, суть не изменится.
.>Каким образом разбить? Как распределить по коду? Как назвать куски? Это и есть вопрос слоёв и архитектуры.
Детерминированные функции и extension-методы. Если у тебя набор функций, то нет особой надобности делить это на много слоев с кучей уровней косвенности. Решение о создании классов лучше принимать на основе необходимости, а не на основе "паттернов".

.>Можно написать

.>
.>int f(int a, b, c)
.>{
.>   return a + b - c;
.>}
.>

.>а можно написать
.>
.>int calculateAmountToCharge(int orderPrice, deliveryPrice, discount)
.>{
.>  int totalPrice = orderPrice + deliveryPrice;
.>  return totalPrice - discount;
.>}
.>

Ну не пиши так. Кто-то заставляет?

.>>>Давай напиши окончательный вариант аналогичный моему и сравним читабельность и устойчивость к изменениям. Классы, которые у меня не реализованы (SomeSecurity, SessionService, CommunicationService, AuditSerivce, Database), будем считать писались другой командой, у тебя только есть интерфейсы которые ты волен задать сам. Что должно быть:


.>>>* конкретные ошибки (не ололо 500, всё пропало) NotFoundException, SuicideNotAllowedException,

G>>Это неверный подход. Надо не позволять пользователю выполнить такие операции, скрывать\деактивировать кнопки на UI. А если же какойнить хакер сделает такой вызов, то "все пропало" достаточно.
.>Я говорю о UI разработчиках, пользователях API.
А UI не пользователь API?

.>>>* вызов проверки безопасности someSecurity.checkSomething

G>>Так вообще писать нельзя. Проверки ролей безопасности должны быть в фильрах (атрибутах), а проверки данных в запросе.
.>А фильтры|атрибуты как работают? Магически опять? Или нужно кучу кода?
.>Да и вообще, фильтры|атрибуты или просто вызов метода — не принципиально, детали реализации. Суть архитектуры остаётся той же.
Суть да, форма — нет. Если у тебя A вызывает Б, Б вызывает В, а В делает запрос к базе и больше ничего, то нет смыса иметь такую косвеннсть. Запрос можно сделать прямо в А или, на крайняк в Б, если это приведет у уменьшению кода.
Твой подход оправдывает только то, что у тебя много чего делается еще на каждом уровне. Но на практике эти cross-cutting concerns надо выносить отдельно, иначе у тебя появится много копипасты.
Представь, что у тебя в someSecurity.checkSomething добавится еще один параметр, тебе придется поменять все методы, где он вызывается и все юнит-тесты для этих методов. Если же у тебя этот вызов спрятан внутри атрибута-фильра, то надо будет в одном месте поменять.

.>>>* аудит auditSerivce.reportAction

.>>>* посылка сообщения sendMessage
G>>Зачем? это ты уже сам придумал.
.>Да, придумал. Допустим надо. Сложно это тебе добавить в код? Ведь он так гибок к изменениям. Главное на тестах съэкономить.
Вот так я бы делал http://gandjustas.blogspot.com/2010/02/entity-framework.html
Причем оно никак бы не отразилось на моем коде, который я писал выше.

.>>>* Плюс каким образом будут задаваться всякие опции (коннекты к базам, stmp порт, етс).

G>>web.config
.>Допустим. А как оно в код попадёт? Заинжекится фреймворком?
Да, данные из конфига попадают в соотвествующие классы без участия программиста.

.>>>И никакой магии, когда какой-то очередной волшебный фреймворк тебе сам всё сделает.

G>>В смысле? Не пользоваться тем, что есть и фигачить свое? Это же прямой путь к говнокоду.
.>Я не предлагаю не пользоваться фреймворками, а как планировать архитектуру. Понятно с .net, там потратили несколько человеколет, выдали фреймворк, где достаточно написать одну функцию, и всё работает. Суслика видишь?
Не вижу, нет его. Зачем писать больше, если можно писать меньше и делать код надежнее?
В этом и есть основной поинт ТС. Куча готовых средств делают ненужным 99% архитектурных паттернов, а программисты продолжают лепить репозитории, слои, спецификации и кучу хрени, а оправдывают это тестируемостью.
Если бы не было ASP.NET MVC и EF, то программы очевидно выглядели бы по-другому. Но эти средства есть и крайне глупо их не использовать.


G>>>>очень неусточивая к изменениям.

.>>>К каким например? Тесты есть, менять нестрашно.
G>>Добавь дополнительный параметр в метод деактивации. окажется что надо сделать изменение на каждом слое, что в свою очередь вызовет правку сервисов каждого слоя.
G>>Нуегонафиг.
.>Опять... "дополнительный параметр", а бизнес-требования то какие?
А какая разница? Ты можешь гарантировать, что параметр не добавится? Нет, никто не может.
Добавление полей — самая частая доработка,

.>Да и в конце-концов... невелика беда. Добавление нового параметра в 10 функций займёт одну минуту.

Агащаз. Я недавно добавлял одно поле в базу в приложении с такой архитектурой как ты пишешь. Поле использовалось в одном методе, а вот чтобы вывести его в таблицы и формы UI потребовалось поправить 2 метода контроллера, 2 ViewModel, 4 метода сервисов, три хранимки. Также это поломало 4 теста, причем логика, для которой нужно было это поле, даже не тестировалась. Заняло почти день работы.

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

.>>>>>Куда ты дел аудит, посылку сообщения, проверку на то, что юзер уже деакивирован? Куда потерялся факт, что есть две базы — для данных и аудита?

G>>>>У ТС не было такого, это ты уже сам придумал.
.>>>Верно, для того, чтобы поменять одно поле в БД, слои не нужны, можно прямо в коде представления зафигачить апдейт базы. Как я понял вопрос топика — накой вообще все эти слои. Я дополнил требования, чтобы каждый слой имел смысл, выполняя свою роль.
G>>Нет, ты придумал сложности там где их не было. Собственно так и происходит чаще всего в приложении. Приудмываются сложности, потом героически решаются. А надо было всего-то user.Active = false сделать.
.>Ок.
.>
.>  void deactivateUser(long userId)
.>  {
.>     jdbcOperations.update("update user set active=false where id=?", userId);
.>  }
.>

.>одна строчка. asp.net фтопку.
В ASP.NET можно сделать тоже самое. Но тогда ты не застрахован от переименований и банальных ошибок в тексте запроса.

.>>>>>Как сформируется правильное описание ошибки, что юзер с таким id не найден?

G>>>>Это не нужно, ибо чтобы вызвать post с id нужно этот id получить. Если же такого id не существует, то "ололопыщьпыщьвсепропало" достаточно.
.>>>Ты никогда не писал public API? Когда тебя забрасывают вопросами "а что это такое http 500?".
G>>Для web api будет так:
G>>
G>>var user = await ctx.Users.FirstOrDefaultAsync(u => u.Id == userId);
G>>if(user == null) return NotFound();
G>>user.Active = false;
G>>await ctx.SaveChangesAsync();
G>>return Ok();
G>>

.>Уже лучше, осталось остальное реализовать и понять, что кода у тебя будет не меньше для той же функциональности.
А почему у меня не будет такой функцинальности? Кто-то отменяет ASP.NET MVC? Как раз наоборот, развивают.
В том и дело что уже есть такая функциональность. Не надо велосипеды изобретать.

.>>>И это тоже будет в asp.net-обработчике?

G>>Если используется в нескольоких местах, то делается extension-метод:
G>>
G>>public static IQueryable<User> WithoutOpenOrders(this IQueryable<User> source)
G>>{
G>>    return source.Where(u => !u.Orders.Any(o => o.CloseDate == null)); 
G>>}
G>>

G>>Далее код пишется так:
G>>
G>>ctx.Users.WithoutOpenOrders().First(u => u.Id == userId);
G>>

G>>Получается еще и DSL, что прекрасно.
.>Согласен. Можно и так. Осталось это объединить в одном namespace и внезапно получится UserDao.
А почему Dao? Это вполне себе бизнес-логика, то есть логика, нужная бизнесу. Да и как не называй, это не слои приложения, ибо нету косвенности.

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

G>>Это не тот случай изначально.
.>Раз в этом формуе, то хочется верить в лучшее, что человек хочет что-то новое изучить, чтобы мог пойти работать в такую компанию. А так пусть пишет на PHP, и никаких забот.
А чем в принципе PHP отличается от Java или ASP.NET? Языки разные, а приложения в общем те же — прослойка между базой и UI.
Re[9]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 16.11.14 02:46
Оценка:
Здравствуйте, AndrewJD, Вы писали:

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

AJD>И сложные и/или тормознутые.

Сложные и тормознутые в контексте тестирования попадают в совершенно разные категории. Сложность компонента/метода на использование его в тестах не отражается никоим образом. А вот тормознутость может привести к принципипльно иным решениям в тестировании приложения. Даже тот же DI в этом случае может оказаться полезным, хотя всегда можно обойтись без него.

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

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

Если этот компонент часть приложения, в котором тестированию уделяется серьёзное внимание, то скорее всего этот компонент тоже обложен тестами вдоль и поперёк. Т.е сложное окружение для его тестирования уже имеется в наличии. В этом случае никто не запрещает использование техник повторного использования кода для воспроизведения сложного окружения. Это один вариант. Второй — использование в тестируемом методе уже готовых результатов этого компонента, а не его самого. Обычно в правильно спроектированном коде отчуждение таких мест от основного workflow не представляет особых проблем.
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 16.11.14 03:19
Оценка:
Здравствуйте, ., Вы писали:

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

.>Эээ. По тестам видно — что.

Т.е. у нас проблемы с пониманием что такое расширяемое приложение?

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

.>"недетерминированным во времени" — запросто, проект-то меняется.

Это как раз самое простое. Сложнее когда тест зависит от внешних компонент, которые, например, могут быть временно недоступны.

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

.>А что такого выпедрючного в DI?

Ну хотя бы в том, что под элементарную передачу параметра в конструктор или присвоение свойству значения подвели целую псевдо теорию, которую обозвали DI. Есть мнение, что от DI нереально прутся прежде всего те, кому довелось осознать "магию" косвенных вызовов, но так и застрявших на этом уровне. Другими словами, чтобы понять что такое DI нужно стать эльфом 48-го уровня, а чтобы начать осознавать вред наносимый этой хренью нужно достигнуть 60-го уровня.

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

.>Сколько будет аргументов у функции "деактивировать пользователя"?

Сколько?

.>Можешь начинать ржать, но во время ржания пожалуйста напиши мне аналогичный код в котором только "чистая функция, принимающая всё необходимое в качестве параметра и возвращающая результат как возвращаемое значение".


Можно в исполнении LinqToDB?

class UserManager
{
    void DeactivateUser(long userId)
    {
        using (var db = new MyDB())
        {
            db.Users
                .Where(t => t.UserID == userID)
                .Set  (t => t.IsActive, false)
                .Update();
        }
    }
}


Тест:

class UserManagerTest
{
    [Test]
    void DeactivateUserTest(long userId)
    {
        using (var db = new MyDB())
        {
            db.Users
                .Where(t => t.UserID == 1)
                .Set  (t => t.IsActive, true)
                .Update();

            new UserManagment().DeactivateUser(1);

            Assert.That(db.Users.First(t => t.UserID == 1), Is.False);
        }
    }
}

Заметь, из выпендрёжного этот код превратился в примитивный.
Если нам не помогут, то мы тоже никого не пощадим.
Re[14]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 16.11.14 16:07
Оценка:
Здравствуйте, gandjustas, Вы писали:

.>>Каким образом разбить? Как распределить по коду? Как назвать куски? Это и есть вопрос слоёв и архитектуры.

G>Детерминированные функции и extension-методы. Если у тебя набор функций, то нет особой надобности делить это на много слоев с кучей уровней косвенности. Решение о создании классов лучше принимать на основе необходимости, а не на основе "паттернов".
Я так и не увидел функции, которая делает всё, что делает мой слоистый код. Приведённый тобой код аналогичен одному методу моего кода.
У меня все классы stateless, что в общем-то превращает их методы в чистые функции, у которых несколько аргументов перенесены в конструктор.

G>Ну не пиши так. Кто-то заставляет?

Ты однако демонстируешь код в одну сторчку как образец. Зачем косвенность в виде локальной переменной? Можно ведь в одну строчку написать.

.>>>>* конкретные ошибки (не ололо 500, всё пропало) NotFoundException, SuicideNotAllowedException,

G>>>Это неверный подход. Надо не позволять пользователю выполнить такие операции, скрывать\деактивировать кнопки на UI. А если же какойнить хакер сделает такой вызов, то "все пропало" достаточно.
.>>Я говорю о UI разработчиках, пользователях API.
G>А UI не пользователь API?
Слава КПСС вообще не человек. Нормальные ошибки нужны только человекам. Я говорю о UI разработчиках! Именно для них мы делаем вменяемый публичный API, которым они смогут нормально пользоваться, не пиная нас каждый раз, когда видят невменяемый ответ от сервера.

.>>А фильтры|атрибуты как работают? Магически опять? Или нужно кучу кода?

.>>Да и вообще, фильтры|атрибуты или просто вызов метода — не принципиально, детали реализации. Суть архитектуры остаётся той же.
G>Суть да, форма — нет. Если у тебя A вызывает Б, Б вызывает В, а В делает запрос к базе и больше ничего, то нет смыса иметь такую косвеннсть. Запрос можно сделать прямо в А или, на крайняк в Б, если это приведет у уменьшению кода.
Косвенность там не просто так, а для разделения обязанностей.

G>Твой подход оправдывает только то, что у тебя много чего делается еще на каждом уровне. Но на практике эти cross-cutting concerns надо выносить отдельно, иначе у тебя появится много копипасты.

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

G>Представь, что у тебя в someSecurity.checkSomething добавится еще один параметр, тебе придется поменять все методы, где он вызывается и все юнит-тесты для этих методов.

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

G>Если же у тебя этот вызов спрятан внутри атрибута-фильра, то надо будет в одном месте поменять.

Ужас, а атрибуту-фильтру передают параметры телепатией, через ноосферу.

G>>>Зачем? это ты уже сам придумал.

.>>Да, придумал. Допустим надо. Сложно это тебе добавить в код? Ведь он так гибок к изменениям. Главное на тестах съэкономить.
G>Вот так я бы делал http://gandjustas.blogspot.com/2010/02/entity-framework.html
G>Причем оно никак бы не отразилось на моем коде, который я писал выше.
Это не совсем аудит, а логгирование, или в лучшем случае сохранение истории. Записи аудита обычно диктуются бизнесом и связаны с действиями пользователя, а не полями в БД.

.>>>>* Плюс каким образом будут задаваться всякие опции (коннекты к базам, stmp порт, етс).

G>>>web.config
.>>Допустим. А как оно в код попадёт? Заинжекится фреймворком?
G>Да, данные из конфига попадают в соотвествующие классы без участия программиста.
Не понял. Ведь мы без IoC контейнеров и DI забанили. А вдруг выясняется что зависимость инжектится IoC фреймоврком. Уличная магия какая-то.

.>>Я не предлагаю не пользоваться фреймворками, а как планировать архитектуру. Понятно с .net, там потратили несколько человеколет, выдали фреймворк, где достаточно написать одну функцию, и всё работает. Суслика видишь?

G>Не вижу, нет его. Зачем писать больше, если можно писать меньше и делать код надежнее?
G>В этом и есть основной поинт ТС. Куча готовых средств делают ненужным 99% архитектурных паттернов, а программисты продолжают лепить репозитории, слои, спецификации и кучу хрени, а оправдывают это тестируемостью.
G>Если бы не было ASP.NET MVC и EF, то программы очевидно выглядели бы по-другому. Но эти средства есть и крайне глупо их не использовать.
Что значит ненужным? Просто MVC и EF и есть те самые архитектурные паттерны. Т.е. ты хочешь сказать, что программисты не разобравшись с тем, что уже есть начинают лепить своё? Тогда да, согласен — жуть. С таким я не спорю.
Хотя не понимаю, тесты то тут причём? Понятное дело, что тесты для MVC и EF писать не надо, ибо внешние компоненты, уже, надеюсь протестированные майкрософтом.

.>>Опять... "дополнительный параметр", а бизнес-требования то какие?

G>А какая разница? Ты можешь гарантировать, что параметр не добавится? Нет, никто не может.
Большая. Т.к. решение может быть разное — от тупо добавления одного параметра к одному методу до развёртывания нового сервиса в кластере.

G>Добавление полей — самая частая доработка,

Да и в общем-то самая простая модификация кода. Не понимаю что ты хочешь этим доказать.

.>>Да и в конце-концов... невелика беда. Добавление нового параметра в 10 функций займёт одну минуту.

G>Агащаз. Я недавно добавлял одно поле в базу в приложении с такой архитектурой как ты пишешь. Поле использовалось в одном методе, а вот чтобы вывести его в таблицы и формы UI потребовалось поправить 2 метода контроллера, 2 ViewModel, 4 метода сервисов, три хранимки. Также это поломало 4 теста, причем логика, для которой нужно было это поле, даже не тестировалась. Заняло почти день работы.
Не знаю, трудно судить о суслике которого я не видел. Вот я привёл код, давай его и рассматривать.

G>Ты же не забывай, что у тебя не один метод, как ты написал, а несколько десятков. И в каждом возможны изменения. По факту такое расслоение, которое ты предлагаешь, по факту увеличивает площадь изменений, а в теории должно уменьшать, ведь именно для этого и делается расслоение.

Это способ декомпозиции сложной функциональности на несколько частей, чтобы не было кучи всего в одном месте. SRP, OCP и всё такое.

.>>одна строчка. asp.net фтопку.

G>В ASP.NET можно сделать тоже самое. Но тогда ты не застрахован от переименований и банальных ошибок в тексте запроса.
Застрахован. Тесты есть, которые можно ещё и выполнить в разных окружениях и быть уверенным, что везде работает. Не говоря уж о подсветке, автодополнении, авторефакторингах и прочем.

.>>Уже лучше, осталось остальное реализовать и понять, что кода у тебя будет не меньше для той же функциональности.

G>А почему у меня не будет такой функцинальности? Кто-то отменяет ASP.NET MVC? Как раз наоборот, развивают.
G>В том и дело что уже есть такая функциональность. Не надо велосипеды изобретать.
Я имею в виду ту функциональность, которая в моём коде.
И это хорошо, что тут веб-приложение, поэтому asp подходит, а если что другое, оффлайн клиент какой-нибудь, то уже упс.

.>>Согласен. Можно и так. Осталось это объединить в одном namespace и внезапно получится UserDao.

G>А почему Dao? Это вполне себе бизнес-логика, то есть логика, нужная бизнесу. Да и как не называй, это не слои приложения, ибо нету косвенности.
Потому что бизнес не говорит таким языком, не "дай мне предикат поиска юзеров, у которых нет открытых заказов". Бизнес скажет что-то типа "деактивировать можно только таких юзеров, у которых нет открытых заказов. При ошибке отобразить текст XYZ".

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

G>>>Это не тот случай изначально.
.>>Раз в этом формуе, то хочется верить в лучшее, что человек хочет что-то новое изучить, чтобы мог пойти работать в такую компанию. А так пусть пишет на PHP, и никаких забот.
G>А чем в принципе PHP отличается от Java или ASP.NET? Языки разные, а приложения в общем те же — прослойка между базой и UI.
Что очень маловероятно, PHP будет использоваться в системе, простой которой может нанести серьёзный ущерб.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 16.11.14 16:34
Оценка:
Здравствуйте, IT, Вы писали:

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

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

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

.>>"недетерминированным во времени" — запросто, проект-то меняется.
IT>Это как раз самое простое. Сложнее когда тест зависит от внешних компонент, которые, например, могут быть временно недоступны.
Это будет интеграционный тест. Что вообще параллельно юнит-тесту.

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

.>>А что такого выпедрючного в DI?
IT>Ну хотя бы в том, что под элементарную передачу параметра в конструктор или присвоение свойству значения подвели целую псевдо теорию, которую обозвали DI. Есть мнение, что от DI нереально прутся прежде всего те, кому довелось осознать "магию" косвенных вызовов, но так и застрявших на этом уровне. Другими словами, чтобы понять что такое DI нужно стать эльфом 48-го уровня, а чтобы начать осознавать вред наносимый этой хренью нужно достигнуть 60-го уровня.
Как я понял из вышенаписанного — достичь 60-го уровня можно только если есть какой-нибудь фреймворк типа ASP.NET, и заявлять что DI и фреймворки хрень, игнорируя факт, что этот самый фреймворк делает этот самый DI.

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

.>>Сколько будет аргументов у функции "деактивировать пользователя"?
IT>Сколько?
В моей реализации получилось один — user. Интересно сколько будет в твоей.

.>>Можешь начинать ржать, но во время ржания пожалуйста напиши мне аналогичный код в котором только "чистая функция, принимающая всё необходимое в качестве параметра и возвращающая результат как возвращаемое значение".


IT>Можно в исполнении LinqToDB?

IT>Заметь, из выпендрёжного этот код превратился в примитивный.
Аналог твоего кода ещё примитивнее:
void deactivateUser(long userId)
{
  jdbcOperations.update("update user set active=false where id=?", userId);
}

Тест будет:
void testDeactivateUser()
{
  jdbcOperations.update("update user set active=true where id=1");
  new UserDao(jdbcOperations).deactivateUser(1);
  assertThat(jdbcOperations.queryForObject("select active from user where id=1"), is(false));
}

Даже без всяких фреймворков. И что? Я прошу код аналогичный моему по функциональности, с посылкой сообщений, аудитом, различными проверками, обработкой ошибок и т.п.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[15]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.11.14 18:53
Оценка:
Здравствуйте, ., Вы писали:

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


.>>>Каким образом разбить? Как распределить по коду? Как назвать куски? Это и есть вопрос слоёв и архитектуры.

G>>Детерминированные функции и extension-методы. Если у тебя набор функций, то нет особой надобности делить это на много слоев с кучей уровней косвенности. Решение о создании классов лучше принимать на основе необходимости, а не на основе "паттернов".
.>Я так и не увидел функции, которая делает всё, что делает мой слоистый код. Приведённый тобой код аналогичен одному методу моего кода.
Они там и не нужны, ты еще не понял этого? Даже если понадобится функционал, то он будет не в теле методов написан.

.>У меня все классы stateless, что в общем-то превращает их методы в чистые функции, у которых несколько аргументов перенесены в конструктор.

Это неправда. Ты получаешь классы извне, какое там состояние ты не знаешь. Даже ссылка на другой класс — тоже состояние. Я вот один раз сломал несколько тестов в коде подобном твоем, просто потому что часть инициализации из метода перенес в конструктор, естественно поведение при этом не поменялось. Так что не надо желаемое выдавать за действительное.
Ну и основная проблема остается — много уровней косвенности.

.>>>>>* конкретные ошибки (не ололо 500, всё пропало) NotFoundException, SuicideNotAllowedException,

G>>>>Это неверный подход. Надо не позволять пользователю выполнить такие операции, скрывать\деактивировать кнопки на UI. А если же какойнить хакер сделает такой вызов, то "все пропало" достаточно.
.>>>Я говорю о UI разработчиках, пользователях API.
G>>А UI не пользователь API?
.>Слава КПСС вообще не человек. Нормальные ошибки нужны только человекам. Я говорю о UI разработчиках! Именно для них мы делаем вменяемый публичный API, которым они смогут нормально пользоваться, не пиная нас каждый раз, когда видят невменяемый ответ от сервера.
А ты сам не делаешь UI? Ты еще не понял, что лучше человеку не дать нажать кнопку, чем показать потом ошибку? Ты не на том внимание концентируешь.

.>>>А фильтры|атрибуты как работают? Магически опять? Или нужно кучу кода?

.>>>Да и вообще, фильтры|атрибуты или просто вызов метода — не принципиально, детали реализации. Суть архитектуры остаётся той же.
G>>Суть да, форма — нет. Если у тебя A вызывает Б, Б вызывает В, а В делает запрос к базе и больше ничего, то нет смыса иметь такую косвеннсть. Запрос можно сделать прямо в А или, на крайняк в Б, если это приведет у уменьшению кода.
.>Косвенность там не просто так, а для разделения обязанностей.
А разделение обязанностей зачем? Его делают для уменьшения изменений и "наведенных" ошибок. Но лишние уровни косвенности увеличивают площадь изменений и количество наведенных ошибок. Так что шило на мыло.
По сути нет необходимости заниматься таким разделением обязанностей. создавать функции и классы надо для: убирания копипасты, увеличения читаемости, сохранения инвариантов. В твоем случае только читаемость подходит, но создание класса для этого — оверкилл.


G>>Представь, что у тебя в someSecurity.checkSomething добавится еще один параметр, тебе придется поменять все методы, где он вызывается и все юнит-тесты для этих методов.

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

.>А если не нужен, то есть куча других способов — параметр можно сделать с дефолтным значением, создать новый метод или параметр протащить через DI.

Ага, а в нормальных фреймворках это уже встроено, поэтому нет необходимости велосипедить.


G>>Если же у тебя этот вызов спрятан внутри атрибута-фильра, то надо будет в одном месте поменять.

.>Ужас, а атрибуту-фильтру передают параметры телепатией, через ноосферу.
Нет, через контекст.

G>>>>Зачем? это ты уже сам придумал.

.>>>Да, придумал. Допустим надо. Сложно это тебе добавить в код? Ведь он так гибок к изменениям. Главное на тестах съэкономить.
G>>Вот так я бы делал http://gandjustas.blogspot.com/2010/02/entity-framework.html
G>>Причем оно никак бы не отразилось на моем коде, который я писал выше.
.>Это не совсем аудит, а логгирование, или в лучшем случае сохранение истории. Записи аудита обычно диктуются бизнесом и связаны с действиями пользователя, а не полями в БД.
Тогда еще проще — делается атрибутом на Action контроллера.

.>>>>>* Плюс каким образом будут задаваться всякие опции (коннекты к базам, stmp порт, етс).

G>>>>web.config
.>>>Допустим. А как оно в код попадёт? Заинжекится фреймворком?
G>>Да, данные из конфига попадают в соотвествующие классы без участия программиста.
.>Не понял. Ведь мы без IoC контейнеров и DI забанили. А вдруг выясняется что зависимость инжектится IoC фреймоврком. Уличная магия какая-то.
Там все проще — тебе просто передается контекст, их которого ты сам забираешь что нужно. Но можно и IoC прикрутить.

.>>>Я не предлагаю не пользоваться фреймворками, а как планировать архитектуру. Понятно с .net, там потратили несколько человеколет, выдали фреймворк, где достаточно написать одну функцию, и всё работает. Суслика видишь?

G>>Не вижу, нет его. Зачем писать больше, если можно писать меньше и делать код надежнее?
G>>В этом и есть основной поинт ТС. Куча готовых средств делают ненужным 99% архитектурных паттернов, а программисты продолжают лепить репозитории, слои, спецификации и кучу хрени, а оправдывают это тестируемостью.
G>>Если бы не было ASP.NET MVC и EF, то программы очевидно выглядели бы по-другому. Но эти средства есть и крайне глупо их не использовать.
.>Что значит ненужным? Просто MVC и EF и есть те самые архитектурные паттерны. Т.е. ты хочешь сказать, что программисты не разобравшись с тем, что уже есть начинают лепить своё? Тогда да, согласен — жуть. С таким я не спорю.
Начинают лепить обертки, например репозитарии для EF очень популярны. Они по факту ничего полезного не дают, но их все равно лепят практически все.

.>Хотя не понимаю, тесты то тут причём? Понятное дело, что тесты для MVC и EF писать не надо, ибо внешние компоненты, уже, надеюсь протестированные майкрософтом.

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

.>>>Опять... "дополнительный параметр", а бизнес-требования то какие?

G>>А какая разница? Ты можешь гарантировать, что параметр не добавится? Нет, никто не может.
.>Большая. Т.к. решение может быть разное — от тупо добавления одного параметра к одному методу до развёртывания нового сервиса в кластере.
Ты уже начинешь демагогией заниматься. Этот прием называется "обобщение до стирания любых различий". Давай сосредоточимся на маленькой проблеме добавления параметра в вызов метода.

G>>Добавление полей — самая частая доработка,

.>Да и в общем-то самая простая модификация кода. Не понимаю что ты хочешь этим доказать.
В теории самая простая, на практике отнимает неприличное количество времени. Сильно больше чем стоило бы. И причина этому — архитектура, как ты предлагаешь.


G>>Ты же не забывай, что у тебя не один метод, как ты написал, а несколько десятков. И в каждом возможны изменения. По факту такое расслоение, которое ты предлагаешь, по факту увеличивает площадь изменений, а в теории должно уменьшать, ведь именно для этого и делается расслоение.

.>Это способ декомпозиции сложной функциональности на несколько частей, чтобы не было кучи всего в одном месте. SRP, OCP и всё такое.
И зачем оно нужно?

.>>>одна строчка. asp.net фтопку.

G>>В ASP.NET можно сделать тоже самое. Но тогда ты не застрахован от переименований и банальных ошибок в тексте запроса.
.>Застрахован. Тесты есть, которые можно ещё и выполнить в разных окружениях и быть уверенным, что везде работает. Не говоря уж о подсветке, автодополнении, авторефакторингах и прочем.
Тесты говорят что все работает в том окружении, в котором запущено. А когда код попадает в другое окружение — внезапно может перестать. Мало того, что большинство не тестирует код совместно с базой, так еще и раздельный деплой базы и кода часто приводит к несогласованности.

.>>>Уже лучше, осталось остальное реализовать и понять, что кода у тебя будет не меньше для той же функциональности.

G>>А почему у меня не будет такой функцинальности? Кто-то отменяет ASP.NET MVC? Как раз наоборот, развивают.
G>>В том и дело что уже есть такая функциональность. Не надо велосипеды изобретать.
.>Я имею в виду ту функциональность, которая в моём коде.
.>И это хорошо, что тут веб-приложение, поэтому asp подходит, а если что другое, оффлайн клиент какой-нибудь, то уже упс.
В десктопном клиенте такая архитектура вообще не живая.

.>>>Согласен. Можно и так. Осталось это объединить в одном namespace и внезапно получится UserDao.

G>>А почему Dao? Это вполне себе бизнес-логика, то есть логика, нужная бизнесу. Да и как не называй, это не слои приложения, ибо нету косвенности.
.>Потому что бизнес не говорит таким языком, не "дай мне предикат поиска юзеров, у которых нет открытых заказов". Бизнес скажет что-то типа "деактивировать можно только таких юзеров, у которых нет открытых заказов. При ошибке отобразить текст XYZ".
А при чем тут как говорит бизнес? Он вообще так не говорит, даже близко. Но какое это имеет отношение к коду?

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

G>>>>Это не тот случай изначально.
.>>>Раз в этом формуе, то хочется верить в лучшее, что человек хочет что-то новое изучить, чтобы мог пойти работать в такую компанию. А так пусть пишет на PHP, и никаких забот.
G>>А чем в принципе PHP отличается от Java или ASP.NET? Языки разные, а приложения в общем те же — прослойка между базой и UI.
.>Что очень маловероятно, PHP будет использоваться в системе, простой которой может нанести серьёзный ущерб.
Ага, поэтому внезапно на PHP сделаны крупнейшие соцсети, простой которых приводит к непоказу рекламы на несколько миллионов в минуту.
По большому счету нет разницы.
Re[6]: DDD, архитектура и т.д.
От: LeonidV Ниоткуда http://vygovskiy.com
Дата: 16.11.14 20:48
Оценка:
Здравствуйте, vmpire, Вы писали:

V>Через явную передачу интерфейсов, через конфигурационный файл, через специальные фреймворки типа moles...

V>Если вам лично это не нравится — это не значит, что это шлак.
Не очень знаю, что такое moles, но в целом — если только за конфигурацию зависимостей отвечает не зависящий класс (dependant), а кто-то другой (кто считывает конфигурационный файл, например), то это уже IoC.
http://jvmmemory.com — простой способ настройки JVM
Re: DDD, архитектура и т.д.
От: konsoletyper Россия https://github.com/konsoletyper
Дата: 17.11.14 09:00
Оценка:
Здравствуйте, SharpDeveloper, Вы писали:

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

SD>DAL<->BL<->Presentation

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


У меня сложилось впечатление, что DDD — это как MVC, всё о нём говорят, все его используют, но никто в точности не может сказать, что же это такое, и ни у кого нет согласия, чем же считать DDD, у всех оно реализовано по-разному.

Лично я понимаю DDD в максимально широком смысле — это когда разработку приложения начинают с бизнес-логики, а потом уже сбоку прикручивают инфраструктуру (в частности, persistence) и presentation, без вмешательства в саму модель. А всё остальное из этого вытекает:


Такая схема получается достаточно удобной, boilerplate-кода практически нет, риски получить inconsistency практически нулевые, а для пользователя система достаточно отзывчива.
Re[7]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 17.11.14 10:16
Оценка:
Здравствуйте, ., Вы писали:

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

V>>Через явную передачу интерфейсов,
.>А как делается явная передача интерфейса, не через DI ли?
Например, параметром конструктора класса.

V>>через конфигурационный файл,

.>Это как? Очередной xml?
Да, но он уже есть штатно.

V>>через специальные фреймворки типа moles...

.>Как он работает? Как переопределять статики?
http://research.microsoft.com/en-us/projects/moles/

V>>Если вам лично это не нравится — это не значит, что это шлак.

.>Потому что это делает код теста особенным. А хороший тест это просто обычный код, демонстрирующий как твой класс можно использовать, а не как можно надругаться над средой исполнения.
Не делает, если в коде всё делать так же

V>>Не "повезло", а "разумно спроектировано"

.>Code talks
Автор: .
Дата: 15.11.14
, покажи как спроектировать разумно.

Вы хотите, чтобы я, не зная вашего проекта по отдельным разрозненным кускам кода догадался до решаемой задачи в целом и предложил оптимальное решение?
Из того фрагмента, что я вижу мне непонятно, чем плох просто ADO.NET завёрнутый, если нужно в REST или SOAP web service.
Re[7]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 17.11.14 10:23
Оценка:
Здравствуйте, LeonidV, Вы писали:

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


V>>Через явную передачу интерфейсов, через конфигурационный файл, через специальные фреймворки типа moles...

V>>Если вам лично это не нравится — это не значит, что это шлак.
LV>Не очень знаю, что такое moles, но в целом — если только за конфигурацию зависимостей отвечает не зависящий класс (dependant), а кто-то другой (кто считывает конфигурационный файл, например), то это уже IoC.
Там за это отвечает просто пользовательский код. Можно его вызвать из клиента, можно откуда-то ещё... Это уж как программу написать
Re[8]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 17.11.14 12:40
Оценка:
Здравствуйте, vmpire, Вы писали:

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


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

V>>>Через явную передачу интерфейсов,
.>>А как делается явная передача интерфейса, не через DI ли?
V>Например, параметром конструктора класса.
Ты вообще знаешь что такое DI? Каким местом это не DI?

V>>>через конфигурационный файл,

.>>Это как? Очередной xml?
V>Да, но он уже есть штатно.
Т.е. если нам микрософт дала фреймворк да с IoC контейнером, то это значит можно громко заявлять, что IoC, DI и прочие фреймворки не нужны, т.к. они отстой и баззворд. Интересно получается, ASP.NET поклонничество какое-то.

V>>>через специальные фреймворки типа moles...

.>>Как он работает? Как переопределять статики?
V>http://research.microsoft.com/en-us/projects/moles/
Можно привести более конкретную ссылку? У меня вполне точные вопросы.
Мне интересно как он работает внутре. Например, вот был приведён код выше:
IT>
IT>class UserManager
IT>{
IT>    void DeactivateUser(long userId)
IT>    {
IT>        using (var db = new MyDB())
IT>        {
IT>            db.Users
IT>                .Where(t => t.UserID == userID)
IT>                .Set  (t => t.IsActive, false)
IT>                .Update();
IT>        }
IT>    }
IT>}
IT>

Как с помощью moles замокать ситуацию, когда MyDB кидает исключение, например timeout какой-нибудь или Update() ловит constraint violation от базы?

V>>>Если вам лично это не нравится — это не значит, что это шлак.

.>>Потому что это делает код теста особенным. А хороший тест это просто обычный код, демонстрирующий как твой класс можно использовать, а не как можно надругаться над средой исполнения.
V>Не делает, если в коде всё делать так же
Вот там был ещё код:
G>>
G>>public static IQueryable<User> WithoutOpenOrders(this IQueryable<User> source)
G>>{
G>>    return source.Where(u => !u.Orders.Any(o => o.CloseDate == null)); 
G>>}
G>>


Как в этом коде
G>>
G>>ctx.Users.WithoutOpenOrders().First(u => u.Id == userId);
G>>

Смоделировать, что WithoutOpenOrders всегда возвращает true или false, т.к. это значительно упростит тесты — не понадобится во время тестирования конкретного юнита заводить список заказов удовлетворяющий предикату.

V>>>Не "повезло", а "разумно спроектировано"

.>>Code talks
Автор: .
Дата: 15.11.14
, покажи как спроектировать разумно.

V>Вы хотите, чтобы я, не зная вашего проекта по отдельным разрозненным кускам кода догадался до решаемой задачи в целом и предложил оптимальное решение?
Требования вполне читаются из кода. Я выше
Автор: .
Дата: 15.11.14
их уже описывал словесно. Есть конкретные вопросы — спрашивай.

V>Из того фрагмента, что я вижу мне непонятно, чем плох просто ADO.NET завёрнутый, если нужно в REST или SOAP web service.

Не понял что значит завёрнутый. Суть в том, что помимо собственно работы с базой, там делаются всякие проверки, записывается аудит и шлётся сообщение.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 17.11.14 15:08
Оценка:
Здравствуйте, gandjustas, Вы писали:

Упустил реплику когда отвечал.

.>>...поискипано

.>>Тут логики никакой, никаких if, тупой последовательный код, юнит-тесты не нужны, но можно интеграционные.
G>Ты серьезно считаешь что такой код надо писать? А если тебе понадобится добавить инфу кто и когда деактивировал юзера, придется все сверху до низу менять? Это такая шутка?
Конечно нет, поменяется только имплементация AuditService.reportAction (что в общем-то естественно и очевидно). Будет что-то типа
class AuditService
{
 final SessionService sessionService;
 final AuditDao auditDao;
 final TimeService timeService;
 void reportAction(AuditOperation op, long id)
 {
   Principal changeAuthor = sessionService.getPrincipal();
   DateTime whenDateTime = timeService.getNow();
   auditDao.save(new AuditRecord(op, id, changeAuthor, whenDateTime));
 }
}

TimeService — конечно полушутка, обычно пишется DateTime.now() или просто в БД на поле current timestamp вешается, просто демонстрация как любые сервисы легко инжектить.
Потом, можно иметь несколько имплементаций AuditSerivce, один как тут — для веба, другой, скажем, для периодической задачи, запускаемой где-то совсем в другом месте, запускаемой по таймеру, там понятие "кто" может быть другим, auditDao может делать что-то другое (просто в файл писать, а не в бд).
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[16]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 17.11.14 15:57
Оценка:
Здравствуйте, gandjustas, Вы писали:

.>>Я так и не увидел функции, которая делает всё, что делает мой слоистый код. Приведённый тобой код аналогичен одному методу моего кода.

G>Они там и не нужны, ты еще не понял этого? Даже если понадобится функционал, то он будет не в теле методов написан.
Так показывай свой функционал.

.>>У меня все классы stateless, что в общем-то превращает их методы в чистые функции, у которых несколько аргументов перенесены в конструктор.

G>Это неправда. Ты получаешь классы извне, какое там состояние ты не знаешь. Даже ссылка на другой класс — тоже состояние. Я вот один раз сломал несколько тестов в коде подобном твоем, просто потому что часть инициализации из метода перенес в конструктор, естественно поведение при этом не поменялось. Так что не надо желаемое выдавать за действительное.
Трудно на такое что-то внятное ответить. Хз что там у тебя желаемое и действительное. Демонстрируй что-то более конкретное.

G>Ну и основная проблема остается — много уровней косвенности.

Косвеность там двухуровневая — депенденси и его использование. Или я не понимаю что ты там под косвенностью понимаешь.

.>>Слава КПСС вообще не человек. Нормальные ошибки нужны только человекам. Я говорю о UI разработчиках! Именно для них мы делаем вменяемый публичный API, которым они смогут нормально пользоваться, не пиная нас каждый раз, когда видят невменяемый ответ от сервера.

G>А ты сам не делаешь UI? Ты еще не понял, что лучше человеку не дать нажать кнопку, чем показать потом ошибку? Ты не на том внимание концентируешь.
Нет, на это есть более дешевые программисты, если просто браузер или флеш.
А в последнем проекте, в котором я принимал участие, UI у нас делался не только другими девами, но и другой компанией. Было два UI — нативные аппликухи для Andoid и iOS, мы ни то, ни другое не знаем на должном уровне, заоутсорсили другой конторе.
А так конечно, проект, который тянет один дев от и до, скорее и не потребует такой слоёной архитектуры.

.>>Косвенность там не просто так, а для разделения обязанностей.

G>А разделение обязанностей зачем? Его делают для уменьшения изменений и "наведенных" ошибок. Но лишние уровни косвенности увеличивают площадь изменений и количество наведенных ошибок. Так что шило на мыло.
G>По сути нет необходимости заниматься таким разделением обязанностей. создавать функции и классы надо для: убирания копипасты, увеличения читаемости, сохранения инвариантов. В твоем случае только читаемость подходит, но создание класса для этого — оверкилл.
Непонятно почему. Продемострируй альтернативу.
Если просто все методы запихать в один класс... Ну не знаю, даже на моём примере получится объект-всемогутер, типичный антипаттерн.

G>>>Представь, что у тебя в someSecurity.checkSomething добавится еще один параметр, тебе придется поменять все методы, где он вызывается и все юнит-тесты для этих методов.

.>>Если параметр и правда нужен — то это означает, что эти все методы и вправду логически меняются, а значит их надо переписать и протестировать и т.п.
G>Вовсе не означает. Иначе странная логика — любое изменение код, даже очень масштабное является неизбежной необходимостью, потому что "и вправду логически меняются".
G>Стремиться уменьшать площадь изменений нужно независимо от мнения.
Зависимость не от мнения, а от требований. Если мы вдруг мышек должны переделать в ёжиков, то мудрого стратегического совета не хватит, надо тупо будет переделывать. Если что-то маленькое, то и изменение будет маленькое. Масштаб изменения сооизмерим с масштабом нового требования.

.>>А если не нужен, то есть куча других способов — параметр можно сделать с дефолтным значением, создать новый метод или параметр протащить через DI.

G>Ага, а в нормальных фреймворках это уже встроено, поэтому нет необходимости велосипедить.
В смысле если у нас есть фреймворк, который уже есть, то нам фреймворк не нужен. Трудно поспорить.

G>>>Если же у тебя этот вызов спрятан внутри атрибута-фильра, то надо будет в одном месте поменять.

.>>Ужас, а атрибуту-фильтру передают параметры телепатией, через ноосферу.
G>Нет, через контекст.
Это который у меня в примере ApplicationContext? Удивил.

G>>>Вот так я бы делал http://gandjustas.blogspot.com/2010/02/entity-framework.html

G>>>Причем оно никак бы не отразилось на моем коде, который я писал выше.
.>>Это не совсем аудит, а логгирование, или в лучшем случае сохранение истории. Записи аудита обычно диктуются бизнесом и связаны с действиями пользователя, а не полями в БД.
G>Тогда еще проще — делается атрибутом на Action контроллера.
Или вызовом метода. Какая разница? Детали имплементации. Для атрибута всё равно обработчик писать. Кстати, тестировать атрибуты гораздо сложнее, да и гибкость у них не ахти. Если условие вдруг понадобится какое, то уже оппа, ещё один атрибут. В итоге получится attribute oriented programming.

G>>>>>web.config

.>>>>Допустим. А как оно в код попадёт? Заинжекится фреймворком?
G>>>Да, данные из конфига попадают в соотвествующие классы без участия программиста.
.>>Не понял. Ведь мы без IoC контейнеров и DI забанили. А вдруг выясняется что зависимость инжектится IoC фреймоврком. Уличная магия какая-то.
G>Там все проще — тебе просто передается контекст, их которого ты сам забираешь что нужно. Но можно и IoC прикрутить.
Ага, и это внезапно называется Service Locator, убогая альтернатива DI при реализации принципа IoC. Матчасть читали?

.>>Что значит ненужным? Просто MVC и EF и есть те самые архитектурные паттерны. Т.е. ты хочешь сказать, что программисты не разобравшись с тем, что уже есть начинают лепить своё? Тогда да, согласен — жуть. С таким я не спорю.

G>Начинают лепить обертки, например репозитарии для EF очень популярны. Они по факту ничего полезного не дают, но их все равно лепят практически все.
В большом распределённом проекте вполне может быть оправдано. Но, конечно, универсального ответа быть не может, каждый проект может иметь свои особенности.

.>>Хотя не понимаю, тесты то тут причём? Понятное дело, что тесты для MVC и EF писать не надо, ибо внешние компоненты, уже, надеюсь протестированные майкрософтом.

G>Если у тебя есть мощный фреймворк, то тебе становится сложно писать тесты на свой код. Даже если технически можно сделать моки на методы фреймворка, то повторить поведение уже становится крайне сложно.
Фреймворк, который затрудняет написание тестов — фтопку однозначно. Хотя обычно мощные фреймворки предоставляют моки для упрощения тестирования, особенно если это что-то более менее современное, а не старинное отложение мамонта.

G>>>А какая разница? Ты можешь гарантировать, что параметр не добавится? Нет, никто не может.

.>>Большая. Т.к. решение может быть разное — от тупо добавления одного параметра к одному методу до развёртывания нового сервиса в кластере.
G>Ты уже начинешь демагогией заниматься. Этот прием называется "обобщение до стирания любых различий". Давай сосредоточимся на маленькой проблеме добавления параметра в вызов метода.
Ну добавь. Ты не поставил конкретной задачи, как я могу дать конкретный ответ? Что параметр делает? Какое бизнес-требование?

G>>>Добавление полей — самая частая доработка,

.>>Да и в общем-то самая простая модификация кода. Не понимаю что ты хочешь этим доказать.
G>В теории самая простая, на практике отнимает неприличное количество времени. Сильно больше чем стоило бы. И причина этому — архитектура, как ты предлагаешь.
Не знаю, у меня другой опыт. Если хочешь конкретный анализ конкретной проблемы — приведи конкретный пример.

.>>Это способ декомпозиции сложной функциональности на несколько частей, чтобы не было кучи всего в одном месте. SRP, OCP и всё такое.

G>И зачем оно нужно?
Чтобы сложная функциональность выражалась простым кодом, а не god-like методы или классы, "есть слона по кусочкам".

G>>>В ASP.NET можно сделать тоже самое. Но тогда ты не застрахован от переименований и банальных ошибок в тексте запроса.

.>>Застрахован. Тесты есть, которые можно ещё и выполнить в разных окружениях и быть уверенным, что везде работает. Не говоря уж о подсветке, автодополнении, авторефакторингах и прочем.
G> Тесты говорят что все работает в том окружении, в котором запущено. А когда код попадает в другое окружение — внезапно может перестать. Мало того, что большинство не тестирует код совместно с базой, так еще и раздельный деплой базы и кода часто приводит к несогласованности.
Тесты можно запускать где угодно, сколько раз угодно, в каких угодно окружениях. Это же автоматические тесты.

.>>Я имею в виду ту функциональность, которая в моём коде.

.>>И это хорошо, что тут веб-приложение, поэтому asp подходит, а если что другое, оффлайн клиент какой-нибудь, то уже упс.
G>В десктопном клиенте такая архитектура вообще не живая.
Хз, у меня другой опыт. Переписывали "dephi-style" архитектуру на вменяемое, с DI, получалось лучше. Особенно круто получалось, когда одни и те же классы переиспользовались на сервере и occasionally connected клиенте.

G>>>А почему Dao? Это вполне себе бизнес-логика, то есть логика, нужная бизнесу. Да и как не называй, это не слои приложения, ибо нету косвенности.

.>>Потому что бизнес не говорит таким языком, не "дай мне предикат поиска юзеров, у которых нет открытых заказов". Бизнес скажет что-то типа "деактивировать можно только таких юзеров, у которых нет открытых заказов. При ошибке отобразить текст XYZ".
G>А при чем тут как говорит бизнес? Он вообще так не говорит, даже близко. Но какое это имеет отношение к коду?
Притом что идеальный код читается как спека от BA.

.>>Что очень маловероятно, PHP будет использоваться в системе, простой которой может нанести серьёзный ущерб.

G>Ага, поэтому внезапно на PHP сделаны крупнейшие соцсети, простой которых приводит к непоказу рекламы на несколько миллионов в минуту.
G>По большому счету нет разницы.
В общем-то да, но это редкое исключение. Да и вроде только одна соцсеть, насколько я знаю. И соцсеть не несёт финансовой ответственности. Ха-ха, реклама, не надо путать убыток с "недополученной прибылью". И лояльность клиентов потерять непросто.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 17.11.14 16:00
Оценка:
Здравствуйте, ., Вы писали:

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


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


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

V>>>>Через явную передачу интерфейсов,
.>>>А как делается явная передача интерфейса, не через DI ли?
V>>Например, параметром конструктора класса.
.>Ты вообще знаешь что такое DI? Каким местом это не DI?
Во-первых, мы на брудершафт не пили.
Во вторых — где вы нашли утверждение, что это не DI?
Перечитайте ещё раз мой пост, но теперь внимательно: "И то и другое прекрасно реализуется без DI фреймворков"

V>>>>через конфигурационный файл,

.>>>Это как? Очередной xml?
V>>Да, но он уже есть штатно.
.>Т.е. если нам микрософт дала фреймворк да с IoC контейнером, то это значит можно громко заявлять, что IoC, DI и прочие фреймворки не нужны, т.к. они отстой и баззворд. Интересно получается, ASP.NET поклонничество какое-то.
ASP.NET — то тут каким местом?
И эта фраза была вообще не про DI framework. Вы уж если отвечаете, то читайте, на что именно.

V>>>>через специальные фреймворки типа moles...

.>>>Как он работает? Как переопределять статики?
V>>http://research.microsoft.com/en-us/projects/moles/
.>Можно привести более конкретную ссылку? У меня вполне точные вопросы.
Куда конкретнее — там прямо код на экране. И ссылка на видео с подробностями

.>Мне интересно как он работает внутре. Например, вот был приведён код выше:

...
.>Как с помощью moles замокать ситуацию, когда MyDB кидает исключение, например timeout какой-нибудь или Update() ловит constraint violation от базы?
Замокать обращение к MySql и кинутьоттуда что нужно.
Писать конкретный код за вас у меня сейчас времени нет, тем более, что вам это, похоже, всё равно не интересно.

G>>>
G>>>public static IQueryable<User> WithoutOpenOrders(this IQueryable<User> source)
G>>>{
G>>>    return source.Where(u => !u.Orders.Any(o => o.CloseDate == null)); 
G>>>}
G>>>


.>Как в этом коде

G>>>
G>>>ctx.Users.WithoutOpenOrders().First(u => u.Id == userId);
G>>>

.>Смоделировать, что WithoutOpenOrders всегда возвращает true или false, т.к. это значительно упростит тесты — не понадобится во время тестирования конкретного юнита заводить список заказов удовлетворяющий предикату.
Нужно замокать WithoutOpenOrders, очевидно. Или через какой-нибудь mock framework или вручную

V>>>>Не "повезло", а "разумно спроектировано"

.>>>Code talks
Автор: .
Дата: 15.11.14
, покажи как спроектировать разумно.

V>>Вы хотите, чтобы я, не зная вашего проекта по отдельным разрозненным кускам кода догадался до решаемой задачи в целом и предложил оптимальное решение?
.>Требования вполне читаются из кода. Я выше
Автор: .
Дата: 15.11.14
их уже описывал словесно. Есть конкретные вопросы — спрашивай.

Я не вижу требований по использованию EF, не вижу требований к деплойменту. Я даже не вижу требования использовать в продукте .NET, если на то пошло.

V>>Из того фрагмента, что я вижу мне непонятно, чем плох просто ADO.NET завёрнутый, если нужно в REST или SOAP web service.

.>Не понял что значит завёрнутый. Суть в том, что помимо собственно работы с базой, там делаются всякие проверки, записывается аудит и шлётся сообщение.
И что мешает это сделать на ADO.NET? Зачем тут вообще все эти навороты
Re[9]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.11.14 16:13
Оценка:
Здравствуйте, diez_p, Вы писали:

G>>Не нужно ничего. От слова вообще.

G>>Права разруливает фреймворк, транзакционность — EF. Сама логика тривиальна, покрывать юнит-тестами не за чем.
_>Для гомогенного шуточного проекта пойдет.
_>Для системы все может быть сильно сложнее
В реальных системах логика на 80% и более примитивна — проверить права (атрибут)+изменить пару полей, как в примерах выше. Но почти каждый программист считает себя обязанным завернуть одну строку бизнес-логики в 100500 репозиториев-сервисов-адаптеров-фасадов.

_>приведенный код напоминает нуб-дельфи-стайл
OnButtonClick { /* поехали писать в базу, пусть даже и в другом потоке*/ }

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

_>Код точки нравится больше. Если мне надо лезть в работу с пользователями, для этого есть UserManager, который знает или делегирует знания кому надо.

В этом и проблема. Код не должен нравится, он должен приносить деньги. Излишняя косвенность деньги тратит.
Re[9]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.11.14 16:14
Оценка:
Здравствуйте, ., Вы писали:

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


.>Упустил реплику когда отвечал.


.>>>...поискипано

.>>>Тут логики никакой, никаких if, тупой последовательный код, юнит-тесты не нужны, но можно интеграционные.
G>>Ты серьезно считаешь что такой код надо писать? А если тебе понадобится добавить инфу кто и когда деактивировал юзера, придется все сверху до низу менять? Это такая шутка?
.>Конечно нет, поменяется только имплементация AuditService.reportAction (что в общем-то естественно и очевидно).
Не в аудит добавить, а в юзера. Аудит вообще не нужен.
Re[17]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.11.14 16:54
Оценка:
Здравствуйте, ., Вы писали:

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


.>>>Я так и не увидел функции, которая делает всё, что делает мой слоистый код. Приведённый тобой код аналогичен одному методу моего кода.

G>>Они там и не нужны, ты еще не понял этого? Даже если понадобится функционал, то он будет не в теле методов написан.
.>Так показывай свой функционал.
Так я показал — надо обновить поле в бд. Больше ничего не надо.

G>>Ну и основная проблема остается — много уровней косвенности.

.>Косвеность там двухуровневая — депенденси и его использование. Или я не понимаю что ты там под косвенностью понимаешь.
Это значит, что реальная работа (обновление поля) делается на третьем уровне вложенности вызовов. Я видел паталогические случаи с 15 уровнями вложенности. Система по функционалу была примитивная, но сложность её была запредельная.


.>А так конечно, проект, который тянет один дев от и до, скорее и не потребует такой слоёной архитектуры.

А для скльки надо?


.>>>Косвенность там не просто так, а для разделения обязанностей.

G>>А разделение обязанностей зачем? Его делают для уменьшения изменений и "наведенных" ошибок. Но лишние уровни косвенности увеличивают площадь изменений и количество наведенных ошибок. Так что шило на мыло.
G>>По сути нет необходимости заниматься таким разделением обязанностей. создавать функции и классы надо для: убирания копипасты, увеличения читаемости, сохранения инвариантов. В твоем случае только читаемость подходит, но создание класса для этого — оверкилл.
.>Непонятно почему. Продемострируй альтернативу.
Ну вот ты пример кода привел и я. Теперь надо при деактивации в поле пользователя записать кто его деактивировал и причину.
Сколько изменений понадобится в твоем коде?

.>Если просто все методы запихать в один класс... Ну не знаю, даже на моём примере получится объект-всемогутер, типичный антипаттерн.

А зачем все методы пихать в один класс? Надо не создавать методов вообще. А если создавать, то чистые функции, не используя объекты. Для чистых функций все равно сколько их в одном классе.

G>>>>Представь, что у тебя в someSecurity.checkSomething добавится еще один параметр, тебе придется поменять все методы, где он вызывается и все юнит-тесты для этих методов.

.>>>Если параметр и правда нужен — то это означает, что эти все методы и вправду логически меняются, а значит их надо переписать и протестировать и т.п.
G>>Вовсе не означает. Иначе странная логика — любое изменение код, даже очень масштабное является неизбежной необходимостью, потому что "и вправду логически меняются".
G>>Стремиться уменьшать площадь изменений нужно независимо от мнения.
.>Зависимость не от мнения, а от требований. Если мы вдруг мышек должны переделать в ёжиков, то мудрого стратегического совета не хватит, надо тупо будет переделывать. Если что-то маленькое, то и изменение будет маленькое. Масштаб изменения сооизмерим с масштабом нового требования.
Так вот изменение то маленькое — один дополнительный параметр в someSecurity.checkSomething, а менять придется все методы и все тесты этих методов.

.>>>А если не нужен, то есть куча других способов — параметр можно сделать с дефолтным значением, создать новый метод или параметр протащить через DI.

G>>Ага, а в нормальных фреймворках это уже встроено, поэтому нет необходимости велосипедить.
.>В смысле если у нас есть фреймворк, который уже есть, то нам фреймворк не нужен. Трудно поспорить.
То самописный фреймворк не нужен. Тем не менее, ты именно с этим утверждением и начал спорить.

G>>>>Если же у тебя этот вызов спрятан внутри атрибута-фильра, то надо будет в одном месте поменять.

.>>>Ужас, а атрибуту-фильтру передают параметры телепатией, через ноосферу.
G>>Нет, через контекст.
.>Это который у меня в примере ApplicationContext? Удивил.
Я хз что там у тебя в примере.

G>>>>Вот так я бы делал http://gandjustas.blogspot.com/2010/02/entity-framework.html

G>>>>Причем оно никак бы не отразилось на моем коде, который я писал выше.
.>>>Это не совсем аудит, а логгирование, или в лучшем случае сохранение истории. Записи аудита обычно диктуются бизнесом и связаны с действиями пользователя, а не полями в БД.
G>>Тогда еще проще — делается атрибутом на Action контроллера.
.>Или вызовом метода. Какая разница?
Большая разница. Вызов метода — гораздо более сильная связь, чем навешивание атрибута. Следовательно площадь изменений разная.

.>Детали имплементации. Для атрибута всё равно обработчик писать.

Атрибут и есть обработчик

.>Кстати, тестировать атрибуты гораздо сложнее, да и гибкость у них не ахти.

Вот оно. Снова тестирование вспомнили.
Но по факту тестировать атрибут не сложнее, чем любой другой код. Пока нет завязки на внешнюю нетривиальную систему, то все тестируется одинаково.

.>Если условие вдруг понадобится какое, то уже оппа, ещё один атрибут. В итоге получится attribute oriented programming.

Да, и это прекрасно. Лучше 10 атрибутов, чем 10 ссылок на другие объекты.



G>>>>>>web.config

.>>>>>Допустим. А как оно в код попадёт? Заинжекится фреймворком?
G>>>>Да, данные из конфига попадают в соотвествующие классы без участия программиста.
.>>>Не понял. Ведь мы без IoC контейнеров и DI забанили. А вдруг выясняется что зависимость инжектится IoC фреймоврком. Уличная магия какая-то.
G>>Там все проще — тебе просто передается контекст, их которого ты сам забираешь что нужно. Но можно и IoC прикрутить.
.>Ага, и это внезапно называется Service Locator, убогая альтернатива DI при реализации принципа IoC.
И чего в ней такого убогого? Твой код и так зависит от фреймворка, еще одна зависимость от контекста ситуацию не изменит. Если ты сам пишешь код, то лучше DI, чем DL — связей меньше.

.>Матчасть читали?

И даже писали http://gandjustas.blogspot.com/2011/08/ioc-aop-unity.html

.>>>Что значит ненужным? Просто MVC и EF и есть те самые архитектурные паттерны. Т.е. ты хочешь сказать, что программисты не разобравшись с тем, что уже есть начинают лепить своё? Тогда да, согласен — жуть. С таким я не спорю.

G>>Начинают лепить обертки, например репозитарии для EF очень популярны. Они по факту ничего полезного не дают, но их все равно лепят практически все.
.>В большом распределённом проекте вполне может быть оправдано. Но, конечно, универсального ответа быть не может, каждый проект может иметь свои особенности.
"Большой распределенный проект" по большей части состоит из такой же примитивной логики с изменением одного поля в базе. В чем там оправданность фасадов-репозитариев-сервисов-адаптеров?

.>>>Хотя не понимаю, тесты то тут причём? Понятное дело, что тесты для MVC и EF писать не надо, ибо внешние компоненты, уже, надеюсь протестированные майкрософтом.

G>>Если у тебя есть мощный фреймворк, то тебе становится сложно писать тесты на свой код. Даже если технически можно сделать моки на методы фреймворка, то повторить поведение уже становится крайне сложно.
.>Фреймворк, который затрудняет написание тестов — фтопку однозначно. Хотя обычно мощные фреймворки предоставляют моки для упрощения тестирования, особенно если это что-то более менее современное, а не старинное отложение мамонта.
Любой мощный фреймворк однозначно затрудняет. Ибо обладает поведением, которое ты не можешь тривиально воспроизвести. А если фреймворк не обладает таким поведением, то он не сильно и нужен.

G>>>>А какая разница? Ты можешь гарантировать, что параметр не добавится? Нет, никто не может.

.>>>Большая. Т.к. решение может быть разное — от тупо добавления одного параметра к одному методу до развёртывания нового сервиса в кластере.
G>>Ты уже начинешь демагогией заниматься. Этот прием называется "обобщение до стирания любых различий". Давай сосредоточимся на маленькой проблеме добавления параметра в вызов метода.
.>Ну добавь. Ты не поставил конкретной задачи, как я могу дать конкретный ответ? Что параметр делает? Какое бизнес-требование?
Давай. Параметр City, передается к клиента в куке (не очень safe, но для прототипа сойдет), при проверке нучно учтывать, что город пользователя в профиле совпадает с параметром в куке.


G>>>>Добавление полей — самая частая доработка,

.>>>Да и в общем-то самая простая модификация кода. Не понимаю что ты хочешь этим доказать.
G>>В теории самая простая, на практике отнимает неприличное количество времени. Сильно больше чем стоило бы. И причина этому — архитектура, как ты предлагаешь.
.>Не знаю, у меня другой опыт. Если хочешь конкретный анализ конкретной проблемы — приведи конкретный пример.

.>>>Это способ декомпозиции сложной функциональности на несколько частей, чтобы не было кучи всего в одном месте. SRP, OCP и всё такое.

G>>И зачем оно нужно?
.>Чтобы сложная функциональность выражалась простым кодом, а не god-like методы или классы, "есть слона по кусочкам".
Сложная фукнционалность не может быть выражена простым кодом, иначе это не сложная функциональность.
Но я так понимаю, что ты хочешь добиться увеличения читаемости — так для этого с головой достаточно чистых фунций. Получить такие функции легко — пишешь все в одном месте, потом делаешь extract method и переделываешь их в static для повторяющихся кусков. Если функции нужны более чем в оном контроллере — делаешь extract class.
В принципе даже сложные системы можно можно написать в таком духе.

G>>>>В ASP.NET можно сделать тоже самое. Но тогда ты не застрахован от переименований и банальных ошибок в тексте запроса.

.>>>Застрахован. Тесты есть, которые можно ещё и выполнить в разных окружениях и быть уверенным, что везде работает. Не говоря уж о подсветке, автодополнении, авторефакторингах и прочем.
G>> Тесты говорят что все работает в том окружении, в котором запущено. А когда код попадает в другое окружение — внезапно может перестать. Мало того, что большинство не тестирует код совместно с базой, так еще и раздельный деплой базы и кода часто приводит к несогласованности.
.>Тесты можно запускать где угодно, сколько раз угодно, в каких угодно окружениях. Это же автоматические тесты.
Тока не на продашене, а как раз там ошибки деплоймента и случаются

.>>>Я имею в виду ту функциональность, которая в моём коде.

.>>>И это хорошо, что тут веб-приложение, поэтому asp подходит, а если что другое, оффлайн клиент какой-нибудь, то уже упс.
G>>В десктопном клиенте такая архитектура вообще не живая.
.>Хз, у меня другой опыт. Переписывали "dephi-style" архитектуру на вменяемое, с DI, получалось лучше. Особенно круто получалось, когда одни и те же классы переиспользовались на сервере и occasionally connected клиенте.
Если переписать говно, написанное студентом, то в любом случае станет лучше. Даже если получится такое же говно по формальных характеристиками, но это будет уже свое говно.

G>>>>А почему Dao? Это вполне себе бизнес-логика, то есть логика, нужная бизнесу. Да и как не называй, это не слои приложения, ибо нету косвенности.

.>>>Потому что бизнес не говорит таким языком, не "дай мне предикат поиска юзеров, у которых нет открытых заказов". Бизнес скажет что-то типа "деактивировать можно только таких юзеров, у которых нет открытых заказов. При ошибке отобразить текст XYZ".
G>>А при чем тут как говорит бизнес? Он вообще так не говорит, даже близко. Но какое это имеет отношение к коду?
.>Притом что идеальный код читается как спека от BA.
Я вот что-то не знаю BA, которые в спеках пишут про сохранение данных, репозитории и императивные проверки.
Да и в ценность этого?

.>>>Что очень маловероятно, PHP будет использоваться в системе, простой которой может нанести серьёзный ущерб.

G>>Ага, поэтому внезапно на PHP сделаны крупнейшие соцсети, простой которых приводит к непоказу рекламы на несколько миллионов в минуту.
G>>По большому счету нет разницы.
.>В общем-то да, но это редкое исключение. Да и вроде только одна соцсеть, насколько я знаю. И соцсеть не несёт финансовой ответственности. Ха-ха, реклама, не надо путать убыток с "недополученной прибылью". И лояльность клиентов потерять непросто.
Недополученная прибыть — это убыток, ибо капитальные затраты уже понесены, да и людям ЗП платить надо. Недополученная прибыль не будет убытком, только если у тебя реальные затраты во время простоя равны нулю. Но в реальности такого не бывает.
А что касается систем, то кроме соцсетей я видел много других систем на PHP — микрокредиты, онлайн-бухгалтерии и системы учета, да и сотни других бизнес-систем.
Re[10]: DDD, архитектура и т.д.
От: diez_p  
Дата: 17.11.14 21:46
Оценка:
_>>Код точки нравится больше. Если мне надо лезть в работу с пользователями, для этого есть UserManager, который знает или делегирует знания кому надо.
G>В этом и проблема. Код не должен нравится, он должен приносить деньги. Излишняя косвенность деньги тратит.
Просто когда надо отключить юзера, разорвать сессии в текущем контексте, отправить уведомление в другие сисемы. И вот тут никакой хендлер контроллера не годится. Опять таки у вас все пишется и читается в базу, а если есть кэши?
Каждый отовсюду сможет их перетряхивать, в общем сильно много но.
Приведенный код подойдет для форума, ну или еще какой-то простой веб системы.

на счет код должен работать, это да, но код на порядок чаще читают, чем пишут и по этому он должен быть читаемым и понятным. И понятным в первую очередь для расширения. Например задизейблить группу пользователей.
Re[11]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.11.14 00:43
Оценка:
Здравствуйте, diez_p, Вы писали:

_>>>Код точки нравится больше. Если мне надо лезть в работу с пользователями, для этого есть UserManager, который знает или делегирует знания кому надо.

G>>В этом и проблема. Код не должен нравится, он должен приносить деньги. Излишняя косвенность деньги тратит.
_>Просто когда надо отключить юзера, разорвать сессии в текущем контексте, отправить уведомление в другие сисемы.И вот тут никакой хендлер контроллера не годится.
Надо сделать три функции. Зачем плодить классы?

_>Опять таки у вас все пишется и читается в базу, а если есть кэши?

А как кеши влияют на необходимость создавать классы? Похоже что никак.

_>Каждый отовсюду сможет их перетряхивать, в общем сильно много но.

А это как относится к вопросу?

_>Приведенный код подойдет для форума, ну или еще какой-то простой веб системы.

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


_>на счет код должен работать, это да, но код на порядок чаще читают, чем пишут и по этому он должен быть читаемым и понятным. И понятным в первую очередь для расширения. Например задизейблить группу пользователей.

Читаемость у кода тем выше, чем меньше строк и сущностей. А названия классов вроде UserManager читаемости не добавляют. Лучше функции делать. Зачастую одна функция, вроде «задизейблить группу пользователей» вызывается ровно в одном экшене одного контроллера. Поэтому даже если ты выделил функцию для читаемости, то создавать класс не имеет смысла.
Re[12]: DDD, архитектура и т.д.
От: diez_p  
Дата: 18.11.14 11:29
Оценка:
Здравствуйте, gandjustas, Вы писали:

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


_>>>>Код точки нравится больше. Если мне надо лезть в работу с пользователями, для этого есть UserManager, который знает или делегирует знания кому надо.

G>>>В этом и проблема. Код не должен нравится, он должен приносить деньги. Излишняя косвенность деньги тратит.
_>>Просто когда надо отключить юзера, разорвать сессии в текущем контексте, отправить уведомление в другие сисемы.И вот тут никакой хендлер контроллера не годится.
G>Надо сделать три функции. Зачем плодить классы?
Класс скажем так несколько иная абстракция чем функция.

_>>Опять таки у вас все пишется и читается в базу, а если есть кэши?

G>А как кеши влияют на необходимость создавать классы? Похоже что никак.
Пишем в базу, читаем с кэша, возникает вопрос синхронизации.


_>>на счет код должен работать, это да, но код на порядок чаще читают, чем пишут и по этому он должен быть читаемым и понятным. И понятным в первую очередь для расширения. Например задизейблить группу пользователей.

G>Читаемость у кода тем выше, чем меньше строк и сущностей. А названия классов вроде UserManager читаемости не добавляют. Лучше функции делать. Зачастую одна функция, вроде «задизейблить группу пользователей» вызывается ровно в одном экшене одного контроллера. Поэтому даже если ты выделил функцию для читаемости, то создавать класс не имеет смысла.
На счет объема кода и его читабельности сильно холиварная тема. Со вторым согласен, если в бизнес области дизейбл юзера такой же тривиальный процесс и вероятность изменения процесса очень мала.
Re[13]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.11.14 15:53
Оценка:
Здравствуйте, diez_p, Вы писали:

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


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


_>>>>>Код точки нравится больше. Если мне надо лезть в работу с пользователями, для этого есть UserManager, который знает или делегирует знания кому надо.

G>>>>В этом и проблема. Код не должен нравится, он должен приносить деньги. Излишняя косвенность деньги тратит.
_>>>Просто когда надо отключить юзера, разорвать сессии в текущем контексте, отправить уведомление в другие сисемы.И вот тут никакой хендлер контроллера не годится.
G>>Надо сделать три функции. Зачем плодить классы?
Именно: класс это данные+фукнции, а в случае с IoC нарваться на неявное состояние можно очень легко. Поэтому детерминированные функции гораздо лучше, чем классы.

_>Класс скажем так несколько иная абстракция чем функция.


_>>>Опять таки у вас все пишется и читается в базу, а если есть кэши?

G>>А как кеши влияют на необходимость создавать классы? Похоже что никак.
_>Пишем в базу, читаем с кэша, возникает вопрос синхронизации.
Видимо надо сбрасывать кеш. Но тут вопрос — по каким ключам сбрасывать кеш? Например ты кешируеь записи по отдельности, а показываешь список. При изменении записи надо сбрасывать и кеш отдельной записи, и кеш списка, а также кеши списков, которые ссылаются на изменяемые записи. То есть просто перенести все в отдельные классы ты не сможешь, ибо код в каждом конкретном случае будет разный. Тогда почему просто не оставить его в контроллере? Возможно в виде отдельных фукнций.


_>>>на счет код должен работать, это да, но код на порядок чаще читают, чем пишут и по этому он должен быть читаемым и понятным. И понятным в первую очередь для расширения. Например задизейблить группу пользователей.

G>>Читаемость у кода тем выше, чем меньше строк и сущностей. А названия классов вроде UserManager читаемости не добавляют. Лучше функции делать. Зачастую одна функция, вроде «задизейблить группу пользователей» вызывается ровно в одном экшене одного контроллера. Поэтому даже если ты выделил функцию для читаемости, то создавать класс не имеет смысла.
_>На счет объема кода и его читабельности сильно холиварная тема. Со вторым согласен, если в бизнес области дизейбл юзера такой же тривиальный процесс и вероятность изменения процесса очень мала.
Возьми текущий проект и посчитай какой процент функций классов *Manager вызываются в одном месте.
Re[10]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 18.11.14 19:47
Оценка:
Здравствуйте, vmpire, Вы писали:

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

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

V>Во вторых — где вы нашли утверждение, что это не DI?

V>Перечитайте ещё раз мой пост, но теперь внимательно: "И то и другое прекрасно реализуется без DI фреймворков"
Я никогда не слышал о DI фреймворках. Что за зверь такой? Слышал о IoC-контейнерах.
В моём коде-примере вообще чистая ява, никаких фреймворков, даже классы JDK не использутся, за исключением java.lang.String.

V>>>>>через конфигурационный файл,

.>>>>Это как? Очередной xml?
V>>>Да, но он уже есть штатно.
.>>Т.е. если нам микрософт дала фреймворк да с IoC контейнером, то это значит можно громко заявлять, что IoC, DI и прочие фреймворки не нужны, т.к. они отстой и баззворд. Интересно получается, ASP.NET поклонничество какое-то.
V>ASP.NET — то тут каким местом?
V>И эта фраза была вообще не про DI framework. Вы уж если отвечаете, то читайте, на что именно.
Я читаю. Вы пишете "Совсем не нужны IoC-и". И вдруг "он[конфигурационный файл] уже есть штатно [в ASP.NET как я понимаю, в c# спеке языка надеюсь такого нет]". А как этот файл работает? Правильно, через IoC-и! Что за каша?
Для справки: DI — способ реализации IoC.

.>>Можно привести более конкретную ссылку? У меня вполне точные вопросы.

V>Куда конкретнее — там прямо код на экране. И ссылка на видео с подробностями
Посмотрел видео. Как я и говорил — хак рантайма, аналог Powermock в java, только как обычно убожество от MS — с xml, dll, naming conventions, кликами мыши и видеоролики показывающие как две строчки тривиального кода написать за пол часа.
Вот аналог java, завидуйте:
@RunWith(PowerMockRunner.class)
@PrepareForTest( { System.class })
public class SystemTimeTest
{
    @Test
    public void testTime()
    {
        PowerMockito.mockStatic(System.class);
        when(System.currentTimeMillis()).thenReturn(42L);
        assertThat(System.currentTimeMillis(), is(42L));
    }
}

Всё, никаких xml/dll/кодогенерации и прочего треша.
Но суть даже не в этом. Будет ли вы это использовать в продакш коде? Нет! И это плохо, т.к. тесты вашего класса будут особенными, в то время когда идеальные тесты — это простой код, который работает точно так же как главный код.

.>>Мне интересно как он работает внутре. Например, вот был приведён код выше:

V>...
.>>Как с помощью moles замокать ситуацию, когда MyDB кидает исключение, например timeout какой-нибудь или Update() ловит constraint violation от базы?
V>Замокать обращение к MySql и кинутьоттуда что нужно.
V>Писать конкретный код за вас у меня сейчас времени нет, тем более, что вам это, похоже, всё равно не интересно.
Уже интересно. Это говорит о развитости технологии и дизайне в целом. Вот ва уже потребовалось знание того, что там MySql оказывается, а не просто MyDB контекст, не важно какой. Называется — сильная связь.
На джава c DI это ровно одна строчка: "when(userDao.updateUser(user)).thenThrow(new TimeoutException())", пишется за пять секунд. А вам приходится время на это отыскивать.

.>>Как в этом коде

G>>>>
G>>>>ctx.Users.WithoutOpenOrders().First(u => u.Id == userId);
G>>>>

.>>Смоделировать, что WithoutOpenOrders всегда возвращает true или false, т.к. это значительно упростит тесты — не понадобится во время тестирования конкретного юнита заводить список заказов удовлетворяющий предикату.
V>Нужно замокать WithoutOpenOrders, очевидно. Или через какой-нибудь mock framework или вручную
Как вручную?
Мой код можно и без фреймворков тестить, но код будет неудобно писать — вместо мокинга методов их придётся переопределять.

V>>>Вы хотите, чтобы я, не зная вашего проекта по отдельным разрозненным кускам кода догадался до решаемой задачи в целом и предложил оптимальное решение?

.>>Требования вполне читаются из кода. Я выше
Автор: .
Дата: 15.11.14
их уже описывал словесно. Есть конкретные вопросы — спрашивай.

V>Я не вижу требований по использованию EF, не вижу требований к деплойменту. Я даже не вижу требования использовать в продукте .NET, если на то пошло.
Требования "использовать EF/.net"? Это кто такие требования выдвигает? MS?
Не важно, пусть вы архитектор. Принимайте сами эти решения, это я буду БА — требования с т.з. бизнеса я уже писал, поищите в треде.

V>>>Из того фрагмента, что я вижу мне непонятно, чем плох просто ADO.NET завёрнутый, если нужно в REST или SOAP web service.

.>>Не понял что значит завёрнутый. Суть в том, что помимо собственно работы с базой, там делаются всякие проверки, записывается аудит и шлётся сообщение.
V>И что мешает это сделать на ADO.NET? Зачем тут вообще все эти навороты
Сделайте. Мы посмотрим.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 18.11.14 20:15
Оценка:
Здравствуйте, IT, Вы писали:

IT>>>Т.е. у нас проблемы с пониманием что такое расширяемое приложение?

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

IT>>>Это как раз самое простое. Сложнее когда тест зависит от внешних компонент, которые, например, могут быть временно недоступны.

.>>Это будет интеграционный тест. Что вообще параллельно юнит-тесту.
IT>А ты в курсе какие тесты имел ввиду топик-стартер?
Топик-стартер вообще не писал о тестах, а ты телепат?

.>>Как я понял из вышенаписанного — достичь 60-го уровня можно только если есть какой-нибудь фреймворк типа ASP.NET, и заявлять что DI и фреймворки хрень, игнорируя факт, что этот самый фреймворк делает этот самый DI.

IT>Не понял мысли.
Что ругаешь фреймворки, но сам с удовольствием используешь asp, видимо он единственный расововерный.

.>>В моей реализации получилось один — user. Интересно сколько будет в твоей.

IT>В моей нет ни одного юзера, есть только UserID.
В коде ниже — то же самое. И код мой проще, не требует фреймворков вообще.

.>>Аналог твоего кода ещё примитивнее:

.>>
.>>void deactivateUser(long userId)
.>>{
.>>  jdbcOperations.update("update user set active=false where id=?", userId);
.>>}
.>>


IT>За такой код в 2014-м году нужно пальцы железной ленейкой отбивать.

А в чём проблема? Чем плох?
Или ты за использование, скажем, питона вообще расстреливаешь?

.>>Даже без всяких фреймворков. И что? Я прошу код аналогичный моему по функциональности, с посылкой сообщений, аудитом, различными проверками, обработкой ошибок и т.п.

IT>Аудита в твоём коде не обнаружено.
AuditServie. Да, имплементации нет, т.к. к делу не относится. считай её пишут другие, тебе дают лишь некий интерфейс.

IT> Если различные проверки — это checkSomething, то желательно раскрыть свою мысль.

"sessionService.getCurrentUser().equals(user) && !user.isMad()", " if(!user.isActive()) return;" ну и поставляемая другим checkSomething, для тебя черный ящик (скажем, вызов к удалённому сервису оценки риска операции).

IT> А с твоим подходом к обработке ошибок я не согласен.

Предложи другой подход. Требование — ошибки должны быть вменяемыми, когда используют твой сервис как чёрный ящик.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.11.14 20:17
Оценка:
Здравствуйте, ., Вы писали:

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


G>>Не в аудит добавить, а в юзера.

.>Как это в юзера? Зачем?? Но в общем не важно, это не конец света, т.к. есть несколько способов, выбирай по обстоятельствам.
.>Между слоями инфу можно передавать несколькими путями:
.>1. Между соседними слоями обычно просто через параметры методов.
.>2. Между далеко разрозненными слоями — через контекст.
.>3. В некоторых случаях когда параметров жутко много (больше ~десяти), то создают объект-структуру для этих параметров, тогда добавление нового поля будет тривиально.

То есть хорошего способа добавления параметра не существует. Так и запишем.

G>>Аудит вообще не нужен.

.>Как не нужен? Повезло, если тебе не нужен. Если в некоторых проектах не будет правильного организованного аудита, то в лучшем случае оштрафуют на несколько сотен миллионов долларов, а то и вообще лавочку прикроют.
Это где такое?
Re[18]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 18.11.14 21:28
Оценка:
Здравствуйте, gandjustas, Вы писали:


.>>Так показывай свой функционал.

G>Так я показал — надо обновить поле в бд. Больше ничего не надо.
Если ты и правда всегда писал проекты, в которых "Больше ничего не надо", то не(?) повезло. Тогда да, я уже привёл код который должен быть: jdbsUperations.update... и он кстати гораздо проще этой мути с EF. Когда придётся поучаствовать в проекте где "надо много чего", то думаю поменяешь своё мнение.

G>>>Ну и основная проблема остается — много уровней косвенности.

.>>Косвеность там двухуровневая — депенденси и его использование. Или я не понимаю что ты там под косвенностью понимаешь.
G>Это значит, что реальная работа (обновление поля) делается на третьем уровне вложенности вызовов. Я видел паталогические случаи с 15 уровнями вложенности. Система по функционалу была примитивная, но сложность её была запредельная.
Простую вещь можно сделать сложно, но это не значит что не бывает сложных вещей.
У меня три уровня потому что делается не только обновление поля, но и куча всего другого. Слои введены не на пустом месте, как иногда бывает.

.>>А так конечно, проект, который тянет один дев от и до, скорее и не потребует такой слоёной архитектуры.

G>А для скльки надо?
Обычно точно после 10-15 человек. Когда одна команда становится слишком большой, придётся делить на несколько, и для уменьшения лагов желательно чтобы команды работали как можно более независимо.

G>>>По сути нет необходимости заниматься таким разделением обязанностей. создавать функции и классы надо для: убирания копипасты, увеличения читаемости, сохранения инвариантов. В твоем случае только читаемость подходит, но создание класса для этого — оверкилл.

.>>Непонятно почему. Продемострируй альтернативу.
G>Ну вот ты пример кода привел и я. Теперь надо при деактивации в поле пользователя записать кто его деактивировал и причину.
G>Сколько изменений понадобится в твоем коде?
"кто" я уже показывал — будет просто деталью имплементации AuditService, т.е. менять ничего не потребуется, просто добавить деталь.
"Причина" — не знаю. Можно больше подробностей? Что там может быть и откуда браться?

.>>Если просто все методы запихать в один класс... Ну не знаю, даже на моём примере получится объект-всемогутер, типичный антипаттерн.

G>А зачем все методы пихать в один класс? Надо не создавать методов вообще. А если создавать, то чистые функции, не используя объекты. Для чистых функций все равно сколько их в одном классе.
Чистые функции не умеют писать в базу, например.

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

G>>>Стремиться уменьшать площадь изменений нужно независимо от мнения.
.>>Зависимость не от мнения, а от требований. Если мы вдруг мышек должны переделать в ёжиков, то мудрого стратегического совета не хватит, надо тупо будет переделывать. Если что-то маленькое, то и изменение будет маленькое. Масштаб изменения сооизмерим с масштабом нового требования.
G>Так вот изменение то маленькое — один дополнительный параметр в someSecurity.checkSomething, а менять придется все методы и все тесты этих методов.
Естественно, ведь он потенциально может быть разным в каждом месте использования — надо протестить, что передаётся правильный. Если в большинстве случаев одинаковый — сделай дефолтное значение.
Кстати, рефакторинг "добавить параметр" делает это тривиальным.

.>>>>А если не нужен, то есть куча других способов — параметр можно сделать с дефолтным значением, создать новый метод или параметр протащить через DI.

G>>>Ага, а в нормальных фреймворках это уже встроено, поэтому нет необходимости велосипедить.
.>>В смысле если у нас есть фреймворк, который уже есть, то нам фреймворк не нужен. Трудно поспорить.
G>То самописный фреймворк не нужен. Тем не менее, ты именно с этим утверждением и начал спорить.
Я стал спорить с тем, что фреймворк обязателен. Практически обязательным является DI паттерн, ибо он хорошо вписывается куда угодно. В моём примере никаких фреймворков нет.

.>>>>Ужас, а атрибуту-фильтру передают параметры телепатией, через ноосферу.

G>>>Нет, через контекст.
.>>Это который у меня в примере ApplicationContext? Удивил.
G>Я хз что там у тебя в примере.
Ну посмотри, кто не даёт.

G>>>Тогда еще проще — делается атрибутом на Action контроллера.

.>>Или вызовом метода. Какая разница?
G>Большая разница. Вызов метода — гораздо более сильная связь, чем навешивание атрибута. Следовательно площадь изменений разная.
Зато метод гораздо более гибкий и функциональный. А связь можно ослабить через интерфейс. Атрибуты полезны только когда они реально часто используются для нескольких целей (например, генерация документации и маппинг урлов), на каждый чих атрибуты лучше не создавать.

.>>Детали имплементации. Для атрибута всё равно обработчик писать.

G>Атрибут и есть обработчик
Как? А что атрибут делает?? Где действия описываются? Или ты имеешь в виду атрибуты asp.net? А если не хватит?

.>>Кстати, тестировать атрибуты гораздо сложнее, да и гибкость у них не ахти.

G>Вот оно. Снова тестирование вспомнили.
G>Но по факту тестировать атрибут не сложнее, чем любой другой код. Пока нет завязки на внешнюю нетривиальную систему, то все тестируется одинаково.
Как? Давай пример.

.>>Если условие вдруг понадобится какое, то уже оппа, ещё один атрибут. В итоге получится attribute oriented programming.

G>Да, и это прекрасно. Лучше 10 атрибутов, чем 10 ссылок на другие объекты.
В чём лучше?

.>>Ага, и это внезапно называется Service Locator, убогая альтернатива DI при реализации принципа IoC.

G>И чего в ней такого убогого? Твой код и так зависит от фреймворка, еще одна зависимость от контекста ситуацию не изменит. Если ты сам пишешь код, то лучше DI, чем DL — связей меньше.
Ты код хоть читал? Покажи строчку где там фреймворк?? Это чистый ява код.

.>>Матчасть читали?

G>И даже писали http://gandjustas.blogspot.com/2011/08/ioc-aop-unity.html
Тогда не понимаю в чём прикол. Зачем для DI обязателен фреймворк?

G>>>Начинают лепить обертки, например репозитарии для EF очень популярны. Они по факту ничего полезного не дают, но их все равно лепят практически все.

.>>В большом распределённом проекте вполне может быть оправдано. Но, конечно, универсального ответа быть не может, каждый проект может иметь свои особенности.
G>"Большой распределенный проект" по большей части состоит из такой же примитивной логики с изменением одного поля в базе. В чем там оправданность фасадов-репозитариев-сервисов-адаптеров?
Чтобы разные части проекта могли писать разные команды или даже компании.

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

G>Любой мощный фреймворк однозначно затрудняет. Ибо обладает поведением, которое ты не можешь тривиально воспроизвести. А если фреймворк не обладает таким поведением, то он не сильно и нужен.
Зато можешь воспроизвести с предоставляемыми фреймворком XXXTester-ами или MockSomething-ами. Либо фрейворк фтопку.

G>>>>>А какая разница? Ты можешь гарантировать, что параметр не добавится? Нет, никто не может.

.>>>>Большая. Т.к. решение может быть разное — от тупо добавления одного параметра к одному методу до развёртывания нового сервиса в кластере.
G>>>Ты уже начинешь демагогией заниматься. Этот прием называется "обобщение до стирания любых различий". Давай сосредоточимся на маленькой проблеме добавления параметра в вызов метода.
.>>Ну добавь. Ты не поставил конкретной задачи, как я могу дать конкретный ответ? Что параметр делает? Какое бизнес-требование?
G>Давай. Параметр City, передается к клиента в куке (не очень safe, но для прототипа сойдет), при проверке нучно учтывать, что город пользователя в профиле совпадает с параметром в куке.
Добавлю ClientInfoService.getCity(), который берёт значение City из куки и заинжектю этот сервис в UserManager, там добавлю условие "if(user.getCity() != clientInfoService.getCity())". Всё.

.>>Чтобы сложная функциональность выражалась простым кодом, а не god-like методы или классы, "есть слона по кусочкам".

G>Сложная фукнционалность не может быть выражена простым кодом, иначе это не сложная функциональность.
G>Но я так понимаю, что ты хочешь добиться увеличения читаемости — так для этого с головой достаточно чистых фунций. Получить такие функции легко — пишешь все в одном месте, потом делаешь extract method и переделываешь их в static для повторяющихся кусков. Если функции нужны более чем в оном контроллере — делаешь extract class.
G>В принципе даже сложные системы можно можно написать в таком духе.
У функций будет десятки аргументов...

G>>> Тесты говорят что все работает в том окружении, в котором запущено. А когда код попадает в другое окружение — внезапно может перестать. Мало того, что большинство не тестирует код совместно с базой, так еще и раздельный деплой базы и кода часто приводит к несогласованности.

.>>Тесты можно запускать где угодно, сколько раз угодно, в каких угодно окружениях. Это же автоматические тесты.
G>Тока не на продашене, а как раз там ошибки деплоймента и случаются
Недавно присоединился к команде, где ошибок деплоймента в production не было больше 4 лет (сколько точно не знаю, т.к. человек у которого я спрашивал столько и работает).
Сделано просто — ежедневно создаётся копия production в staging environment. На staging environment постоянно запускаются все тесты (около 10+ тыс Acceptance Tests, несколько тыс Integration Tests), работают долго, несколько часов. Пока все не проходят — деплоймент не делают. Acceptance Tests написаны на упрощённой java, что их читают и ревьювят QA и BA.
Инструкция деплоймента на production состоит из одного шага "do it".

G>>>В десктопном клиенте такая архитектура вообще не живая.

.>>Хз, у меня другой опыт. Переписывали "dephi-style" архитектуру на вменяемое, с DI, получалось лучше. Особенно круто получалось, когда одни и те же классы переиспользовались на сервере и occasionally connected клиенте.
G>Если переписать говно, написанное студентом, то в любом случае станет лучше. Даже если получится такое же говно по формальных характеристиками, но это будет уже свое говно.
Всё говно. Тогда какая разница как писать? Пиши с DI.

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

G>>>А при чем тут как говорит бизнес? Он вообще так не говорит, даже близко. Но какое это имеет отношение к коду?
.>>Притом что идеальный код читается как спека от BA.
G>Я вот что-то не знаю BA, которые в спеках пишут про сохранение данных, репозитории и императивные проверки.
G>Да и в ценность этого?
То что пишуь BA рисуется в BL. Остальное — "инфраструктура".

.>>В общем-то да, но это редкое исключение. Да и вроде только одна соцсеть, насколько я знаю. И соцсеть не несёт финансовой ответственности. Ха-ха, реклама, не надо путать убыток с "недополученной прибылью". И лояльность клиентов потерять непросто.

G>Недополученная прибыть — это убыток, ибо капитальные затраты уже понесены, да и людям ЗП платить надо. Недополученная прибыль не будет убытком, только если у тебя реальные затраты во время простоя равны нулю. Но в реальности такого не бывает.
G>А что касается систем, то кроме соцсетей я видел много других систем на PHP — микрокредиты, онлайн-бухгалтерии и системы учета, да и сотни других бизнес-систем.
Да, соглашусь, на любом языке можно хорошо писать, даже на brainfuck.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[12]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 18.11.14 21:42
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>То есть хорошего способа добавления параметра не существует. Так и запишем.

А что есть хороший способ по-твоему?

G>>>Аудит вообще не нужен.

.>>Как не нужен? Повезло, если тебе не нужен. Если в некоторых проектах не будет правильного организованного аудита, то в лучшем случае оштрафуют на несколько сотен миллионов долларов, а то и вообще лавочку прикроют.
G>Это где такое?
FCA firm, например. Придёт вот клиент в банк и спросит, почему мой аккаунт деактивирован, я, мол, не смог продать акции вовремя и потерял 100500 денег. Если там ответят "мы не знаем", то можно смело подать в суд и легко выиграть дело, FCA обычно на стороне клиентов.
А если аудит будет содержать слишком много, персональные данные, то можно под DPA 1998 попасть.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[19]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.11.14 22:57
Оценка:
Здравствуйте, ., Вы писали:

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



.>>>Так показывай свой функционал.

G>>Так я показал — надо обновить поле в бд. Больше ничего не надо.
.>Если ты и правда всегда писал проекты, в которых "Больше ничего не надо", то не(?) повезло. Тогда да, я уже привёл код который должен быть: jdbsUperations.update... и он кстати гораздо проще этой мути с EF. Когда придётся поучаствовать в проекте где "надо много чего", то думаю поменяешь своё мнение.
В том то и дело, что я много делал проектов, где "много всего". Если на каждую мелочь плодить классы-интерфейсы-фабрики-адаптеры, то сложность поддержки растет очень быстро, даже если каждый кусочек из "много чего" довольно прост.

G>>>>Ну и основная проблема остается — много уровней косвенности.

.>>>Косвеность там двухуровневая — депенденси и его использование. Или я не понимаю что ты там под косвенностью понимаешь.
G>>Это значит, что реальная работа (обновление поля) делается на третьем уровне вложенности вызовов. Я видел паталогические случаи с 15 уровнями вложенности. Система по функционалу была примитивная, но сложность её была запредельная.
.>Простую вещь можно сделать сложно, но это не значит что не бывает сложных вещей.
И что? Разговор то как раз про простые вещи, а ты без необходимости усложняешь. Собственно много кто так делает.

.>У меня три уровня потому что делается не только обновление поля, но и куча всего другого. Слои введены не на пустом месте, как иногда бывает.

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

.>>>А так конечно, проект, который тянет один дев от и до, скорее и не потребует такой слоёной архитектуры.

G>>А для скльки надо?
.>Обычно точно после 10-15 человек. Когда одна команда становится слишком большой, придётся делить на несколько, и для уменьшения лагов желательно чтобы команды работали как можно более независимо.
Чтобы команды были независимыми надо не слои делать, а фичи. Каждая команда пилит свою фичу снизу до верху. Фичи можно сделать независимыми, слои — почти никогда.

G>>>>По сути нет необходимости заниматься таким разделением обязанностей. создавать функции и классы надо для: убирания копипасты, увеличения читаемости, сохранения инвариантов. В твоем случае только читаемость подходит, но создание класса для этого — оверкилл.

.>>>Непонятно почему. Продемострируй альтернативу.
G>>Ну вот ты пример кода привел и я. Теперь надо при деактивации в поле пользователя записать кто его деактивировал и причину.
G>>Сколько изменений понадобится в твоем коде?
.>"кто" я уже показывал — будет просто деталью имплементации AuditService, т.е. менять ничего не потребуется, просто добавить деталь.
.>"Причина" — не знаю. Можно больше подробностей? Что там может быть и откуда браться?
Еще раз — надо не в аудит, а в поле пользователя. У тебя нет хорошего решения такой проблемы. А такие проблемы на два порядка чаще воникают, чем вообще появляется необходимость аудита.

.>>>Если просто все методы запихать в один класс... Ну не знаю, даже на моём примере получится объект-всемогутер, типичный антипаттерн.

G>>А зачем все методы пихать в один класс? Надо не создавать методов вообще. А если создавать, то чистые функции, не используя объекты. Для чистых функций все равно сколько их в одном классе.
.>Чистые функции не умеют писать в базу, например.
Можно не чистые а детерминированные, чтобы поведение зависело только от параметров. Главное уменьшить количество уровней косвенности и связей через состояние.

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

G>>>>Стремиться уменьшать площадь изменений нужно независимо от мнения.
.>>>Зависимость не от мнения, а от требований. Если мы вдруг мышек должны переделать в ёжиков, то мудрого стратегического совета не хватит, надо тупо будет переделывать. Если что-то маленькое, то и изменение будет маленькое. Масштаб изменения сооизмерим с масштабом нового требования.
А ты заранее масштабы новых требований знаешь? Я сомневаюсь. Поэтому уменьшать площадь изменений надо уменьшать независимо от мнения.


G>>Так вот изменение то маленькое — один дополнительный параметр в someSecurity.checkSomething, а менять придется все методы и все тесты этих методов.

.>Естественно, ведь он потенциально может быть разным в каждом месте использования — надо протестить, что передаётся правильный. Если в большинстве случаев одинаковый — сделай дефолтное значение.
.>Кстати, рефакторинг "добавить параметр" делает это тривиальным.
Ага, только когда надо его добавить в 10 функций или в одну — разница огромная. А потом еще тесты поправить.


.>>>>>А если не нужен, то есть куча других способов — параметр можно сделать с дефолтным значением, создать новый метод или параметр протащить через DI.

G>>>>Ага, а в нормальных фреймворках это уже встроено, поэтому нет необходимости велосипедить.
.>>>В смысле если у нас есть фреймворк, который уже есть, то нам фреймворк не нужен. Трудно поспорить.
G>>То самописный фреймворк не нужен. Тем не менее, ты именно с этим утверждением и начал спорить.
.>Я стал спорить с тем, что фреймворк обязателен. Практически обязательным является DI паттерн, ибо он хорошо вписывается куда угодно. В моём примере никаких фреймворков нет.
Ты написал пример с кучей слоев. Я тебе показал, что фреймворк закрывает потребности и куча слоев не нужна. Ты начал спорить.
Прочитай еще раз первое сообщение в теме. Там человек как раз говорит о бесполезности паттернов с современными фреймворками.

G>>>>Тогда еще проще — делается атрибутом на Action контроллера.

.>>>Или вызовом метода. Какая разница?
G>>Большая разница. Вызов метода — гораздо более сильная связь, чем навешивание атрибута. Следовательно площадь изменений разная.
.>Зато метод гораздо более гибкий и функциональный. А связь можно ослабить через интерфейс. Атрибуты полезны только когда они реально часто используются для нескольких целей (например, генерация документации и маппинг урлов), на каждый чих атрибуты лучше не создавать.
С чего это? Чем явный вызов лучше атрибута?

.>>>Детали имплементации. Для атрибута всё равно обработчик писать.

G>>Атрибут и есть обработчик
.>Как? А что атрибут делает?? Где действия описываются? Или ты имеешь в виду атрибуты asp.net? А если не хватит?
Атрибут — класс. Только он неинтрузивно работает. Вызывающий код не знает об атрибутах, атрибут не знает на какой класс он навешан. Фактически связь обеспечивает фреймворк. Меньше связей — меньше изменений. Правда можно так понасоздавать неявных связей, что еще хуже, чем явные

.>>>Кстати, тестировать атрибуты гораздо сложнее, да и гибкость у них не ахти.

G>>Вот оно. Снова тестирование вспомнили.
G>>Но по факту тестировать атрибут не сложнее, чем любой другой код. Пока нет завязки на внешнюю нетривиальную систему, то все тестируется одинаково.
.>Как? Давай пример.
http://stackoverflow.com/questions/8508190/how-do-i-unit-test-a-custom-actionfilter-in-asp-net-mvc

.>>>Если условие вдруг понадобится какое, то уже оппа, ещё один атрибут. В итоге получится attribute oriented programming.

G>>Да, и это прекрасно. Лучше 10 атрибутов, чем 10 ссылок на другие объекты.
.>В чём лучше?
В том, что меньше связей.

.>>>Ага, и это внезапно называется Service Locator, убогая альтернатива DI при реализации принципа IoC.

G>>И чего в ней такого убогого? Твой код и так зависит от фреймворка, еще одна зависимость от контекста ситуацию не изменит. Если ты сам пишешь код, то лучше DI, чем DL — связей меньше.
.>Ты код хоть читал? Покажи строчку где там фреймворк?? Это чистый ява код.
Ты же сам нашел где-то DL в фреймворке и начал ругать.

.>>>Матчасть читали?

G>>И даже писали http://gandjustas.blogspot.com/2011/08/ioc-aop-unity.html
.>Тогда не понимаю в чём прикол. Зачем для DI обязателен фреймворк?
Ты о чем? Не путай причину и следствие. Не для DI нужен фреймворк, а для фреймворка нужен DI\DL, иначе сложно писать будет код.

G>>>>Начинают лепить обертки, например репозитарии для EF очень популярны. Они по факту ничего полезного не дают, но их все равно лепят практически все.

.>>>В большом распределённом проекте вполне может быть оправдано. Но, конечно, универсального ответа быть не может, каждый проект может иметь свои особенности.
G>>"Большой распределенный проект" по большей части состоит из такой же примитивной логики с изменением одного поля в базе. В чем там оправданность фасадов-репозитариев-сервисов-адаптеров?
.>Чтобы разные части проекта могли писать разные команды или даже компании.
Ты пробовал реально делить слои между командами? Результат ужасен. Можно еще отделить серверный код от клиентского, согласовав между ними интерфейс. Если делить всю разработку по слоям, то скорость внесения изменений упадет до нуля. Особенно при кейсах когда надо поле добавить. Так что тут тоже слои мимо кассы.
Пойми уже наконец, что никакой потребности деления на слои не существует, кроме уменьшения площади изменений. Если этого достигнуть не удается, то и не надо заниматься порнографией.

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

G>>Любой мощный фреймворк однозначно затрудняет. Ибо обладает поведением, которое ты не можешь тривиально воспроизвести. А если фреймворк не обладает таким поведением, то он не сильно и нужен.
.>Зато можешь воспроизвести с предоставляемыми фреймворком XXXTester-ами или MockSomething-ами. Либо фрейворк фтопку.
Еще раз — ты можешь создать моки, но воспроизвести поведение не сможешь.
Вот представь, что у тебя есть storage, который дерагет твой коллбек. Атрибуты файла приходят в виде словаря. Тебе надо на основе атрибутов выполнить некоторое действие.
Какие присходдят атрибуты и какие там заначения определяется сотнями настроек storage и действиями, которые выполняет пользователь. Как ты будешь тестить? Реализация логики формирования набора атрибутов сравнима с реализацей движка.

G>>>>>>А какая разница? Ты можешь гарантировать, что параметр не добавится? Нет, никто не может.

.>>>>>Большая. Т.к. решение может быть разное — от тупо добавления одного параметра к одному методу до развёртывания нового сервиса в кластере.
G>>>>Ты уже начинешь демагогией заниматься. Этот прием называется "обобщение до стирания любых различий". Давай сосредоточимся на маленькой проблеме добавления параметра в вызов метода.
.>>>Ну добавь. Ты не поставил конкретной задачи, как я могу дать конкретный ответ? Что параметр делает? Какое бизнес-требование?
G>>Давай. Параметр City, передается к клиента в куке (не очень safe, но для прототипа сойдет), при проверке нучно учтывать, что город пользователя в профиле совпадает с параметром в куке.
.>Добавлю ClientInfoService.getCity(), который берёт значение City из куки и заинжектю этот сервис в UserManager, там добавлю условие "if(user.getCity() != clientInfoService.getCity())". Всё.
Во все методы UserManager добавишь? Если будет много подобных изменений, то получится монстрик с кучей неявных передач параметров. В итоге идея расслоения перестает рабоать, потому что у тебя сервисы нижнего слоя начинают неявно зависеть от верхних слоев.

.>>>Чтобы сложная функциональность выражалась простым кодом, а не god-like методы или классы, "есть слона по кусочкам".

G>>Сложная фукнционалность не может быть выражена простым кодом, иначе это не сложная функциональность.
G>>Но я так понимаю, что ты хочешь добиться увеличения читаемости — так для этого с головой достаточно чистых фунций. Получить такие функции легко — пишешь все в одном месте, потом делаешь extract method и переделываешь их в static для повторяющихся кусков. Если функции нужны более чем в оном контроллере — делаешь extract class.
G>>В принципе даже сложные системы можно можно написать в таком духе.
.>У функций будет десятки аргументов...
Значит слишком толстая функция получилась.

G>>>> Тесты говорят что все работает в том окружении, в котором запущено. А когда код попадает в другое окружение — внезапно может перестать. Мало того, что большинство не тестирует код совместно с базой, так еще и раздельный деплой базы и кода часто приводит к несогласованности.

.>>>Тесты можно запускать где угодно, сколько раз угодно, в каких угодно окружениях. Это же автоматические тесты.
G>>Тока не на продашене, а как раз там ошибки деплоймента и случаются
.>Недавно присоединился к команде, где ошибок деплоймента в production не было больше 4 лет (сколько точно не знаю, т.к. человек у которого я спрашивал столько и работает).
Значит слишком простой проект.


G>>>>В десктопном клиенте такая архитектура вообще не живая.

.>>>Хз, у меня другой опыт. Переписывали "dephi-style" архитектуру на вменяемое, с DI, получалось лучше. Особенно круто получалось, когда одни и те же классы переиспользовались на сервере и occasionally connected клиенте.
G>>Если переписать говно, написанное студентом, то в любом случае станет лучше. Даже если получится такое же говно по формальных характеристиками, но это будет уже свое говно.
.>Всё говно. Тогда какая разница как писать? Пиши с DI.
Можно писать не говно, с наличием\отсуствием DI корреляция слабая.

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

G>>>>А при чем тут как говорит бизнес? Он вообще так не говорит, даже близко. Но какое это имеет отношение к коду?
.>>>Притом что идеальный код читается как спека от BA.
G>>Я вот что-то не знаю BA, которые в спеках пишут про сохранение данных, репозитории и императивные проверки.
G>>Да и в ценность этого?
.>То что пишуь BA рисуется в BL. Остальное — "инфраструктура".
Хороший BA рисует интерфейс и определяет сценарии пользователя.
Re[13]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.11.14 23:53
Оценка:
Здравствуйте, ., Вы писали:

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


G>>То есть хорошего способа добавления параметра не существует. Так и запишем.

.>А что есть хороший способ по-твоему?
Хороший способ — тот, который позволяет минимизировать площадь изменений.


.>FCA firm, например. Придёт вот клиент в банк и спросит, почему мой аккаунт деактивирован, я, мол, не смог продать акции вовремя и потерял 100500 денег. Если там ответят "мы не знаем", то можно смело подать в суд и легко выиграть дело, FCA обычно на стороне клиентов.

.>А если аудит будет содержать слишком много, персональные данные, то можно под DPA 1998 попасть.
Как раз пара дополнительных полей в сущности
Re[11]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 19.11.14 10:29
Оценка:
Здравствуйте, ., Вы писали:

V>>Во вторых — где вы нашли утверждение, что это не DI?

V>>Перечитайте ещё раз мой пост, но теперь внимательно: "И то и другое прекрасно реализуется без DI фреймворков"
.>Я никогда не слышал о DI фреймворках. Что за зверь такой? Слышал о IoC-контейнерах.
Я имел в виду DI framework как частный случай реализации IoC, извините, если запутал.

.>В моём коде-примере вообще чистая ява, никаких фреймворков, даже классы JDK не использутся, за исключением java.lang.String.

Ну так я о том и пишу — не нужно никаких специальных инструментов, для реализации mocks и stubs для юнит тестов.
Хотя, конечно, в некоторых случаях они могут сэкономить время.


.>Я читаю. Вы пишете "Совсем не нужны IoC-и". И вдруг "он[конфигурационный файл] уже есть штатно [в ASP.NET как я понимаю, в c# спеке языка надеюсь такого нет]". А как этот файл работает? Правильно, через IoC-и! Что за каша?

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

.>Для справки: DI — способ реализации IoC.

Да, поэтому я и написал "DI framework", чтобы более точно обрисовать, о чём я говорю. IoC — более широкое понятие, но я сейчас только от DI.

.>>>Можно привести более конкретную ссылку? У меня вполне точные вопросы.

V>>Куда конкретнее — там прямо код на экране. И ссылка на видео с подробностями
.>Посмотрел видео. Как я и говорил — хак рантайма, аналог Powermock в java, только как обычно убожество от MS — с xml, dll, naming conventions, кликами мыши и видеоролики показывающие как две строчки тривиального кода написать за пол часа.
Это недостаток всех видеоизложений. Информации на две минуты, но рассказывают полчаса.
А в naming convention лично я ничего плохого не вижу.
.>Вот аналог java, завидуйте:
.>
.>@RunWith(PowerMockRunner.class)
.>@PrepareForTest( { System.class })
.>public class SystemTimeTest
.>{
.>    @Test
.>    public void testTime()
.>    {
.>        PowerMockito.mockStatic(System.class);
.>        when(System.currentTimeMillis()).thenReturn(42L);
.>        assertThat(System.currentTimeMillis(), is(42L));
.>    }
.>}
.>

Я большой разницы в количестве кода не вижу:
[TestMethod]
public void TestMethod1()
{
    using (ShimsContext.Create())
    {
        ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);
        DateTime d = DateTime.Now;
        Assert.AreEqual(2000, d.Year);
    }
}

.>Всё, никаких xml/dll/кодогенерации и прочего треша.
Единственное, что нужно дополнительно — это ткнуть мышой в сборку, которую нужно мочить и добавиль для неё .fakes файл, который генерируется автоматически и в котором ничего делать не надо.

.>Но суть даже не в этом. Будет ли вы это использовать в продакш коде? Нет! И это плохо, т.к. тесты вашего класса будут особенными, в то время когда идеальные тесты — это простой код, который работает точно так же как главный код.

С этим согласен, но тут не MS виноват, а сам подход к тому, что мочатся вызываемые методы, вместо того, чтобы их явно отделить.

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

.>Уже интересно. Это говорит о развитости технологии и дизайне в целом. Вот ва уже потребовалось знание того, что там MySql оказывается, а не просто MyDB контекст, не важно какой. Называется — сильная связь.
.>На джава c DI это ровно одна строчка: "when(userDao.updateUser(user)).thenThrow(new TimeoutException())", пишется за пять секунд. А вам приходится время на это отыскивать.
Ну так это не имитация того, что MyDB кидает исключение, это имитация того, что исключение кидает класс userDao.
Это и на Moles пишется на ура. И даже без него, если метод updateUser не статический, а виртуальный.

.>Как вручную?

.>Мой код можно и без фреймворков тестить, но код будет неудобно писать — вместо мокинга методов их придётся переопределять.
Да, вручную это не так удобно, особенно если мочить нужно много.
Но сам факт, что фреймворки для мокинга нужны часто уже признак того, что код, врозможно, слишком сильно связан.


V>>Я не вижу требований по использованию EF, не вижу требований к деплойменту. Я даже не вижу требования использовать в продукте .NET, если на то пошло.

.>Требования "использовать EF/.net"? Это кто такие требования выдвигает? MS?
.>Не важно, пусть вы архитектор. Принимайте сами эти решения, это я буду БА — требования с т.з. бизнеса я уже писал, поищите в треде.
Ну так я выдвину требование: сделать всё максимально просто. И напишу одну строчку
public SetUserInactive()
{
    using(var con = new SqlDbConnection("строка коннекта"))
    {
        con.Open();
        new SqlDbbCommand("UPDATE User SET IsActive = 0 WHERE UserId = 10").ExecuteNoQuery();
    }
}


Если нужна трёхзвенка (хотя, я такого требования не видел), то приписать сверху атрибут [WebMethod] и захостить в каком-нибудь процессе.

V>>>>Из того фрагмента, что я вижу мне непонятно, чем плох просто ADO.NET завёрнутый, если нужно в REST или SOAP web service.

.>>>Не понял что значит завёрнутый. Суть в том, что помимо собственно работы с базой, там делаются всякие проверки, записывается аудит и шлётся сообщение.
V>>И что мешает это сделать на ADO.NET? Зачем тут вообще все эти навороты
.>Сделайте. Мы посмотрим.
Выше
Re[20]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 22.11.14 23:47
Оценка:
Здравствуйте, gandjustas, Вы писали:

.>>Если ты и правда всегда писал проекты, в которых "Больше ничего не надо", то не(?) повезло. Тогда да, я уже привёл код который должен быть: jdbsUperations.update... и он кстати гораздо проще этой мути с EF. Когда придётся поучаствовать в проекте где "надо много чего", то думаю поменяешь своё мнение.

G>В том то и дело, что я много делал проектов, где "много всего". Если на каждую мелочь плодить классы-интерфейсы-фабрики-адаптеры, то сложность поддержки растет очень быстро, даже если каждый кусочек из "много чего" довольно прост.
Альтернативы-то какие? Писать один методы по 500 строк и забивать на тесты?

.>>Простую вещь можно сделать сложно, но это не значит что не бывает сложных вещей.

G>И что? Разговор то как раз про простые вещи, а ты без необходимости усложняешь. Собственно много кто так делает.
Что значит без необходимости? Топик о том, зачем нужно это всё. Конечно, если у тебя простое требование — обновить одно поле в БД, то и код будет простым. Скучно, обсуждать нечего. Я придумал несколько новых требований, которые однострончым кодом уже не выразить и показал как разбиение на слои позволяет получить хороший код, даже в случае сложных вещей. Тут мне начали говорить, что это всё не надо и демонстрировать тот самый тривиальный код из одной строчки. Я прошу кого-нибудь написать более менее сложную вещь (список типичных требований типичной enterprise программы), а не один update, но никто так и не написал. Слов много, дела мало.

.>>У меня три уровня потому что делается не только обновление поля, но и куча всего другого. Слои введены не на пустом месте, как иногда бывает.

G>Конкретно в этом случае у тебя — на пустом. От этого слои становятся бесполезными, ибо не уменьшают площадь изменений.
Так покажи как надо. Но не один апдейт, а все требования которые я уже неоднократно описывал и реализованы в моём коде.

G>>>А для скльки надо?

.>>Обычно точно после 10-15 человек. Когда одна команда становится слишком большой, придётся делить на несколько, и для уменьшения лагов желательно чтобы команды работали как можно более независимо.
G>Чтобы команды были независимыми надо не слои делать, а фичи. Каждая команда пилит свою фичу снизу до верху. Фичи можно сделать независимыми, слои — почти никогда.
А ещё фичи между собой должны взаимодействовать. Потом, представь себе обычное современное web-приложение. Скажем надо поддерживать браузеры (2 человека — скрипты/css/html), надо поддерживать мобильные браузеры (пусть 1 человек), нативные аппликухи для android, ios, winphone, blackberry (6-7 человек), сервер-сайд (2-3 человека). Вот уже 12 человек только девов. Добавь ещё поддержку инфраструктуры — различные environment (dev/test/uat/prod/etc), всякие vcs/ci/repo. И это только инженерные роли. Добавь ещё BA, domain experts, QA, CR, support. Вот и одна тебе фича. Плохо работать в команде, если там больше 8 человек.
Или ты считаешь, что бывают люди которые это сразу всё умеют на должном уровне и двое всё сразу могут запилить?

.>>>>Непонятно почему. Продемострируй альтернативу.

G>>>Ну вот ты пример кода привел и я. Теперь надо при деактивации в поле пользователя записать кто его деактивировал и причину.
G>>>Сколько изменений понадобится в твоем коде?
.>>"кто" я уже показывал — будет просто деталью имплементации AuditService, т.е. менять ничего не потребуется, просто добавить деталь.
.>>"Причина" — не знаю. Можно больше подробностей? Что там может быть и откуда браться?
G>Еще раз — надо не в аудит, а в поле пользователя. У тебя нет хорошего решения такой проблемы. А такие проблемы на два порядка чаще воникают, чем вообще появляется необходимость аудита.
Не понял я. В смысле добавить параметр в deactivateUser? Напугал ежа. Да пожалуйста, одна строчка добавится, ну и один тест:
void deactivateUser(long userId, String gandjustasParam)
{
 ... ---//--- ...
 gandjustasService.doSomethingSpecialForHim(user, gandjustasParam);
 ... ---//--- ...
}


G>>>А зачем все методы пихать в один класс? Надо не создавать методов вообще. А если создавать, то чистые функции, не используя объекты. Для чистых функций все равно сколько их в одном классе.

.>>Чистые функции не умеют писать в базу, например.
G>Можно не чистые а детерминированные, чтобы поведение зависело только от параметров.
В каком месте у меня в коде недерминированность?

G>Главное уменьшить количество уровней косвенности и связей через состояние.

Код в студию, или не было.

.>>>>Зависимость не от мнения, а от требований. Если мы вдруг мышек должны переделать в ёжиков, то мудрого стратегического совета не хватит, надо тупо будет переделывать. Если что-то маленькое, то и изменение будет маленькое. Масштаб изменения сооизмерим с масштабом нового требования.

G>А ты заранее масштабы новых требований знаешь? Я сомневаюсь. Поэтому уменьшать площадь изменений надо уменьшать независимо от мнения.
Код в студию, или не было.

G>>>Так вот изменение то маленькое — один дополнительный параметр в someSecurity.checkSomething, а менять придется все методы и все тесты этих методов.

.>>Естественно, ведь он потенциально может быть разным в каждом месте использования — надо протестить, что передаётся правильный. Если в большинстве случаев одинаковый — сделай дефолтное значение.
.>>Кстати, рефакторинг "добавить параметр" делает это тривиальным.
G>Ага, только когда надо его добавить в 10 функций или в одну — разница огромная. А потом еще тесты поправить.
Если и правда надо в десять, но можно выбрать другой подход. Хотя даже и добавление параметра занимает ~3 секунды. Будет тридцать секунд, невелика потеря. И вообще: Код в студию, или не было.

.>>>>В смысле если у нас есть фреймворк, который уже есть, то нам фреймворк не нужен. Трудно поспорить.

G>>>То самописный фреймворк не нужен. Тем не менее, ты именно с этим утверждением и начал спорить.
.>>Я стал спорить с тем, что фреймворк обязателен. Практически обязательным является DI паттерн, ибо он хорошо вписывается куда угодно. В моём примере никаких фреймворков нет.
G>Ты написал пример с кучей слоев. Я тебе показал, что фреймворк закрывает потребности и куча слоев не нужна. Ты начал спорить.
Я показал что твой однострочный код с фреймворком эквивалентен однострочному коду без фреймворка, но сложнее тестировать. Код эквивалентный по фичам моему ты не показывал.

G>Прочитай еще раз первое сообщение в теме. Там человек как раз говорит о бесполезности паттернов с современными фреймворками.

Он спрашивал зачем слои нужны. Я ответил.

G>>>Большая разница. Вызов метода — гораздо более сильная связь, чем навешивание атрибута. Следовательно площадь изменений разная.

.>>Зато метод гораздо более гибкий и функциональный. А связь можно ослабить через интерфейс. Атрибуты полезны только когда они реально часто используются для нескольких целей (например, генерация документации и маппинг урлов), на каждый чих атрибуты лучше не создавать.
G>С чего это? Чем явный вызов лучше атрибута?
У метода могут быть аргументы, тут же вычисленные, можно возвращаемое значение, обработку исключений и т.п. И тестировать проще.
Атрибут лишь хорош, если тебя в большинстве методов надо делать ровно одно и то же.

G>>>Атрибут и есть обработчик

.>>Как? А что атрибут делает?? Где действия описываются? Или ты имеешь в виду атрибуты asp.net? А если не хватит?
G>Атрибут — класс. Только он неинтрузивно работает. Вызывающий код не знает об атрибутах, атрибут не знает на какой класс он навешан. Фактически связь обеспечивает фреймворк. Меньше связей — меньше изменений. Правда можно так понасоздавать неявных связей, что еще хуже, чем явные
То же и интерфейс. Атрибут для АОП хорош, но это не означает что везде их пихать надо.

G>>>Вот оно. Снова тестирование вспомнили.

G>>>Но по факту тестировать атрибут не сложнее, чем любой другой код. Пока нет завязки на внешнюю нетривиальную систему, то все тестируется одинаково.
.>>Как? Давай пример.
G>http://stackoverflow.com/questions/8508190/how-do-i-unit-test-a-custom-actionfilter-in-asp-net-mvc
Это вроде тест фильтра, который повешен на атрибут, а не наличие и корректность атрибута.

G>>>Да, и это прекрасно. Лучше 10 атрибутов, чем 10 ссылок на другие объекты.

.>>В чём лучше?
G>В том, что меньше связей.
Код в студию, или не было.

G>>>И чего в ней такого убогого? Твой код и так зависит от фреймворка, еще одна зависимость от контекста ситуацию не изменит. Если ты сам пишешь код, то лучше DI, чем DL — связей меньше.

.>>Ты код хоть читал? Покажи строчку где там фреймворк?? Это чистый ява код.
G>Ты же сам нашел где-то DL в фреймворке и начал ругать.
Что-то я упустил. Что за DL? Ты имеешь в виду SL?

.>>>>Матчасть читали?

G>>>И даже писали http://gandjustas.blogspot.com/2011/08/ioc-aop-unity.html
.>>Тогда не понимаю в чём прикол. Зачем для DI обязателен фреймворк?
G>Ты о чем? Не путай причину и следствие. Не для DI нужен фреймворк, а для фреймворка нужен DI\DL, иначе сложно писать будет код.
Нет, DI нужен, а фреймворк опционален.

G>Ты пробовал реально делить слои между командами? Результат ужасен. Можно еще отделить серверный код от клиентского, согласовав между ними интерфейс. Если делить всю разработку по слоям, то скорость внесения изменений упадет до нуля. Особенно при кейсах когда надо поле добавить. Так что тут тоже слои мимо кассы.

Так слои не делятся между командами. Между командами фичи делятся. Но когда все команды распределяют код по одинаковым слоям — легче взаимодействовать.

G>Пойми уже наконец, что никакой потребности деления на слои не существует, кроме уменьшения площади изменений. Если этого достигнуть не удается, то и не надо заниматься порнографией.

Код в студию, или не было.

G>>>Любой мощный фреймворк однозначно затрудняет. Ибо обладает поведением, которое ты не можешь тривиально воспроизвести. А если фреймворк не обладает таким поведением, то он не сильно и нужен.

.>>Зато можешь воспроизвести с предоставляемыми фреймворком XXXTester-ами или MockSomething-ами. Либо фрейворк фтопку.
G>Еще раз — ты можешь создать моки, но воспроизвести поведение не сможешь.
G>Вот представь, что у тебя есть storage, который дерагет твой коллбек. Атрибуты файла приходят в виде словаря. Тебе надо на основе атрибутов выполнить некоторое действие.
G>Какие присходдят атрибуты и какие там заначения определяется сотнями настроек storage и действиями, которые выполняет пользователь. Как ты будешь тестить? Реализация логики формирования набора атрибутов сравнима с реализацей движка.
А зачем воспроизводить поведение в точности? unit-тесты обычно whitebox тесты. Т.е. ты знаешь на какое поведение рассчитывает твой код, так и мочишь.
Набор юнит-тестов показывает какая именно логика реализована и какая будет реакция.
Фреймворки тут не причём. Допустим — операция чтения файла может столько всякого разного поведения выдавать, что закачаешься, но это не означает что это нельзя тестить|мокать. Скажем, вполне возможно ты на рассчитывал на определённый результат при вытаскивании флешки в неподходящий момент. Добавляешь тест, воспроизводящий такое, видишь неверную реакцию твоего кода, правишь код, добиваясь правильного поведения, и смотришь — не сломались ли другие тесты.

G>>>>>>>А какая разница? Ты можешь гарантировать, что параметр не добавится? Нет, никто не может.

.>>>>>>Большая. Т.к. решение может быть разное — от тупо добавления одного параметра к одному методу до развёртывания нового сервиса в кластере.
G>>>>>Ты уже начинешь демагогией заниматься. Этот прием называется "обобщение до стирания любых различий". Давай сосредоточимся на маленькой проблеме добавления параметра в вызов метода.
.>>>>Ну добавь. Ты не поставил конкретной задачи, как я могу дать конкретный ответ? Что параметр делает? Какое бизнес-требование?
G>>>Давай. Параметр City, передается к клиента в куке (не очень safe, но для прототипа сойдет), при проверке нучно учтывать, что город пользователя в профиле совпадает с параметром в куке.
.>>Добавлю ClientInfoService.getCity(), который берёт значение City из куки и заинжектю этот сервис в UserManager, там добавлю условие "if(user.getCity() != clientInfoService.getCity())". Всё.
G>Во все методы UserManager добавишь? Если будет много подобных изменений, то получится монстрик с кучей неявных передач параметров. В итоге идея расслоения перестает рабоать, потому что у тебя сервисы нижнего слоя начинают неявно зависеть от верхних слоев.
Куда потребуется, туда и добавлю, может лучше в securityService подойдёт, я не понимаю точно твоё требование. А ты что предлагаешь? Навешать на все методы магический атрибут?

G>>>Сложная фукнционалность не может быть выражена простым кодом, иначе это не сложная функциональность.

G>>>Но я так понимаю, что ты хочешь добиться увеличения читаемости — так для этого с головой достаточно чистых фунций. Получить такие функции легко — пишешь все в одном месте, потом делаешь extract method и переделываешь их в static для повторяющихся кусков. Если функции нужны более чем в оном контроллере — делаешь extract class.
G>>>В принципе даже сложные системы можно можно написать в таком духе.
.>>У функций будет десятки аргументов...
G>Значит слишком толстая функция получилась.
И что делать?

.>>>>Тесты можно запускать где угодно, сколько раз угодно, в каких угодно окружениях. Это же автоматические тесты.

G>>>Тока не на продашене, а как раз там ошибки деплоймента и случаются
.>>Недавно присоединился к команде, где ошибок деплоймента в production не было больше 4 лет (сколько точно не знаю, т.к. человек у которого я спрашивал столько и работает).
G>Значит слишком простой проект.
Угу, очень разумно делать выводы о сложности проекта по тому, что нет ошибок деплоймента.
На самом деле всё прозаичнее — ошибка в продакшн этого проекта может означать конец бизнеса, репутация — всё, конкуренция жуткая. Поэтому очень много вложено в организацию QC, который максимально автоматизирован.

G>>>Если переписать говно, написанное студентом, то в любом случае станет лучше. Даже если получится такое же говно по формальных характеристиками, но это будет уже свое говно.

.>>Всё говно. Тогда какая разница как писать? Пиши с DI.
G>Можно писать не говно, с наличием\отсуствием DI корреляция слабая.
Не знаю, по моему опыту корреляция есть, хотя о наличии причино-следственной связи я тоже сомневаюсь. Причина наверное компетентность команд.

.>>>>Притом что идеальный код читается как спека от BA.

G>>>Я вот что-то не знаю BA, которые в спеках пишут про сохранение данных, репозитории и императивные проверки.
G>>>Да и в ценность этого?
.>>То что пишуь BA рисуется в BL. Остальное — "инфраструктура".
G>Хороший BA рисует интерфейс и определяет сценарии пользователя.
Хороший дев перерисовывает это один-к-одному в виде BL.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[21]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.11.14 12:02
Оценка:
Здравствуйте, ., Вы писали:

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


.>>>Если ты и правда всегда писал проекты, в которых "Больше ничего не надо", то не(?) повезло. Тогда да, я уже привёл код который должен быть: jdbsUperations.update... и он кстати гораздо проще этой мути с EF. Когда придётся поучаствовать в проекте где "надо много чего", то думаю поменяешь своё мнение.

G>>В том то и дело, что я много делал проектов, где "много всего". Если на каждую мелочь плодить классы-интерфейсы-фабрики-адаптеры, то сложность поддержки растет очень быстро, даже если каждый кусочек из "много чего" довольно прост.
.>Альтернативы-то какие? Писать один методы по 500 строк и забивать на тесты?
Уже выше написал: чисты функции, детерминированные функции.

.>>>Простую вещь можно сделать сложно, но это не значит что не бывает сложных вещей.

G>>И что? Разговор то как раз про простые вещи, а ты без необходимости усложняешь. Собственно много кто так делает.
.>Что значит без необходимости?
То и значит. Ты сам привел код в несколько раз сложнее, чем необходимо для решения задачи, оправдав это какимто неведомым функционалом. Кто гарантирует, что также не произойдет в реальном проекте?

.>Топик о том, зачем нужно это всё. Конечно, если у тебя простое требование — обновить одно поле в БД, то и код будет простым. Скучно, обсуждать нечего. Я придумал несколько новых требований, которые однострончым кодом уже не выразить и показал как разбиение на слои позволяет получить хороший код, даже в случае сложных вещей.

Ага, также из-за того, что "скучно, обсуждать нечего" ты поступишь и в реальном проекте.

.>>>У меня три уровня потому что делается не только обновление поля, но и куча всего другого. Слои введены не на пустом месте, как иногда бывает.

G>>Конкретно в этом случае у тебя — на пустом. От этого слои становятся бесполезными, ибо не уменьшают площадь изменений.
.>Так покажи как надо. Но не один апдейт, а все требования которые я уже неоднократно описывал и реализованы в моём коде.
Я тебе показал как надо. И про атрибуты объяснил. Но ты до сих пор не веришь, что можно написать просто.

G>>>>А для скльки надо?

.>>>Обычно точно после 10-15 человек. Когда одна команда становится слишком большой, придётся делить на несколько, и для уменьшения лагов желательно чтобы команды работали как можно более независимо.
G>>Чтобы команды были независимыми надо не слои делать, а фичи. Каждая команда пилит свою фичу снизу до верху. Фичи можно сделать независимыми, слои — почти никогда.
.>А ещё фичи между собой должны взаимодействовать.
В том и прикол фич, что не должны. Фичи — отделяемая часть функционала, имеющая ценность для пользователя сама по себе. Если фичи функционально связаны, то он не пилятся параллельно.

.>>>>>Непонятно почему. Продемострируй альтернативу.

G>>>>Ну вот ты пример кода привел и я. Теперь надо при деактивации в поле пользователя записать кто его деактивировал и причину.
G>>>>Сколько изменений понадобится в твоем коде?
.>>>"кто" я уже показывал — будет просто деталью имплементации AuditService, т.е. менять ничего не потребуется, просто добавить деталь.
.>>>"Причина" — не знаю. Можно больше подробностей? Что там может быть и откуда браться?
G>>Еще раз — надо не в аудит, а в поле пользователя. У тебя нет хорошего решения такой проблемы. А такие проблемы на два порядка чаще воникают, чем вообще появляется необходимость аудита.
.>Не понял я. В смысле добавить параметр в deactivateUser? Напугал ежа. Да пожалуйста, одна строчка добавится, ну и один тест...
Вранье, у тебя три "слоя" и, соответственно, три передачи параметров. Это значит как минимум в трех местах поменять.


G>>>>А зачем все методы пихать в один класс? Надо не создавать методов вообще. А если создавать, то чистые функции, не используя объекты. Для чистых функций все равно сколько их в одном классе.

.>>>Чистые функции не умеют писать в базу, например.
G>>Можно не чистые а детерминированные, чтобы поведение зависело только от параметров.
.>В каком месте у меня в коде недерминированность?
Во всех методах классов. Каждый метод класса зависит не только от параметров но и от состояния объекта.

G>>Главное уменьшить количество уровней косвенности и связей через состояние.

.>Код в студию, или не было.
Так ты сам приводил код. У тебя там как минимум три уровня косвенности. И это на примитивной задаче.

.>>>>>Зависимость не от мнения, а от требований. Если мы вдруг мышек должны переделать в ёжиков, то мудрого стратегического совета не хватит, надо тупо будет переделывать. Если что-то маленькое, то и изменение будет маленькое. Масштаб изменения сооизмерим с масштабом нового требования.

G>>А ты заранее масштабы новых требований знаешь? Я сомневаюсь. Поэтому уменьшать площадь изменений надо уменьшать независимо от мнения.
.>Код в студию, или не было.
Ты сам приводил код

G>>>>Так вот изменение то маленькое — один дополнительный параметр в someSecurity.checkSomething, а менять придется все методы и все тесты этих методов.

.>>>Естественно, ведь он потенциально может быть разным в каждом месте использования — надо протестить, что передаётся правильный. Если в большинстве случаев одинаковый — сделай дефолтное значение.
.>>>Кстати, рефакторинг "добавить параметр" делает это тривиальным.
G>>Ага, только когда надо его добавить в 10 функций или в одну — разница огромная. А потом еще тесты поправить.
.>Если и правда надо в десять, но можно выбрать другой подход. Хотя даже и добавление параметра занимает ~3 секунды. Будет тридцать секунд, невелика потеря. И вообще: Код в студию, или не было.
Да не занимает оно три секунды. От повторения это правдой не станет. Тебе для начала надо разбораться куда надо добавить и как у тебя control flow и data flow выглядит перед тем как добавлять. А потом еще ошибки будут, тесты править. В лучшем случае полдня в приложении на 10к+ строк.

.>>>>>В смысле если у нас есть фреймворк, который уже есть, то нам фреймворк не нужен. Трудно поспорить.

G>>>>То самописный фреймворк не нужен. Тем не менее, ты именно с этим утверждением и начал спорить.
.>>>Я стал спорить с тем, что фреймворк обязателен. Практически обязательным является DI паттерн, ибо он хорошо вписывается куда угодно. В моём примере никаких фреймворков нет.
G>>Ты написал пример с кучей слоев. Я тебе показал, что фреймворк закрывает потребности и куча слоев не нужна. Ты начал спорить.
.>Я показал что твой однострочный код с фреймворком эквивалентен однострочному коду без фреймворка, но сложнее тестировать. Код эквивалентный по фичам моему ты не показывал.
Тебе еще раз объяснить про атрибуты? Мне лениво писать более трех строк в форуме, особенно если код не решает какую-то проблему.

G>>Прочитай еще раз первое сообщение в теме. Там человек как раз говорит о бесполезности паттернов с современными фреймворками.

.>Он спрашивал зачем слои нужны. Я ответил.
Ага, придумал какие-то сложности и геройски их победил (хотя еще не факт что победил). Так и в реальных приложениях поступают, от этого пост и родился.

G>>>>Большая разница. Вызов метода — гораздо более сильная связь, чем навешивание атрибута. Следовательно площадь изменений разная.

.>>>Зато метод гораздо более гибкий и функциональный. А связь можно ослабить через интерфейс. Атрибуты полезны только когда они реально часто используются для нескольких целей (например, генерация документации и маппинг урлов), на каждый чих атрибуты лучше не создавать.
G>>С чего это? Чем явный вызов лучше атрибута?
.>У метода могут быть аргументы, тут же вычисленные, можно возвращаемое значение, обработку исключений и т.п.
Аргументы откуда берутся? Видимо из запроса, больше не от куда. А в атрибуте есть контекст запроса, из которого те же параметры можно вытащить.

.>И тестировать проще.

Вранье. Спишем просто на незнание.

.>Атрибут лишь хорош, если тебя в большинстве методов надо делать ровно одно и то же.

Так и есть. Мы ведь говорили об аудите, это разве не надо делать в большинстве методов?

G>>>>Атрибут и есть обработчик

.>>>Как? А что атрибут делает?? Где действия описываются? Или ты имеешь в виду атрибуты asp.net? А если не хватит?
G>>Атрибут — класс. Только он неинтрузивно работает. Вызывающий код не знает об атрибутах, атрибут не знает на какой класс он навешан. Фактически связь обеспечивает фреймворк. Меньше связей — меньше изменений. Правда можно так понасоздавать неявных связей, что еще хуже, чем явные
.>То же и интерфейс. Атрибут для АОП хорош, но это не означает что везде их пихать надо.
А я не предлагаю везде, я предлагаю заменить кучи вызовов одинакового функционала на атрибуты. Например проверки, аудиты итп. А после вытаскивания такого cross-cutting функционала становятся не нужны слои, достаточно пары функций в контроллере.

G>>>>Вот оно. Снова тестирование вспомнили.

G>>>>Но по факту тестировать атрибут не сложнее, чем любой другой код. Пока нет завязки на внешнюю нетривиальную систему, то все тестируется одинаково.
.>>>Как? Давай пример.
G>>http://stackoverflow.com/questions/8508190/how-do-i-unit-test-a-custom-actionfilter-in-asp-net-mvc
.>Это вроде тест фильтра, который повешен на атрибут, а не наличие и корректность атрибута.
А тебе надо тестировать наличие атрибута? Совсем двинулся? Посмотреть нельзя?

G>>>>Да, и это прекрасно. Лучше 10 атрибутов, чем 10 ссылок на другие объекты.

.>>>В чём лучше?
G>>В том, что меньше связей.
.>Код в студию, или не было.
Код чего? Ты сам class coupling не можешь померить?

G>>>>И чего в ней такого убогого? Твой код и так зависит от фреймворка, еще одна зависимость от контекста ситуацию не изменит. Если ты сам пишешь код, то лучше DI, чем DL — связей меньше.

.>>>Ты код хоть читал? Покажи строчку где там фреймворк?? Это чистый ява код.
G>>Ты же сам нашел где-то DL в фреймворке и начал ругать.
.>Что-то я упустил. Что за DL? Ты имеешь в виду SL?
Без разницы, хоть DL, хоть SL, хоть ЖL, суть не меняется.


G>>Ты пробовал реально делить слои между командами? Результат ужасен. Можно еще отделить серверный код от клиентского, согласовав между ними интерфейс. Если делить всю разработку по слоям, то скорость внесения изменений упадет до нуля. Особенно при кейсах когда надо поле добавить. Так что тут тоже слои мимо кассы.

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

G>>Пойми уже наконец, что никакой потребности деления на слои не существует, кроме уменьшения площади изменений. Если этого достигнуть не удается, то и не надо заниматься порнографией.

.>Код в студию, или не было.
Оо код чего? Ты же сам привел пример, который очень тяжело поддается изменениям, типа добавить колонку. Зачем тебе еще примеры?

.>А зачем воспроизводить поведение в точности? unit-тесты обычно whitebox тесты. Т.е. ты знаешь на какое поведение рассчитывает твой код, так и мочишь.

Твое знание должно в этом случае совпадать с поведением фреймворка, иначе баги.
Яркий пример. Ты пишешь код, который ориентируется что компонент отдает даты в локали пользователя. Пишешь тест, основанный на таком поведении. А по факту он отдает в UTC, получаешь багу.

.>Набор юнит-тестов показывает какая именно логика реализована и какая будет реакция.

Если ты не повторяешь в точности поведение зависимых компонентов, то вообще ничего не показывает.

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

Я же приводил кейс, читай внимательно. У тебя обработчик события на загрузку файла в storage. В обработчик приходит набор параметров. Сформировать набор параметров для моков по сложности равно разработке storage. Как в этой ситуации unit-тестировать?

.>Куда потребуется, туда и добавлю, может лучше в securityService подойдёт, я не понимаю точно твоё требование.

Да все ты понимаешь, и понимаешь, что такой простой кейс начинает проникать в кучу классов, увеличивая сложность поддержки.

.>А ты что предлагаешь? Навешать на все методы магический атрибут?

Да, по науке это называется AOP. Когда такой cross-cutting concern не пишется явно в коде, а инжектится извне, уменьшая связность и площадь изменений.

.>>>У функций будет десятки аргументов...

G>>Значит слишком толстая функция получилась.
.>И что делать?
Побить на маленькие.

G>>>>Если переписать говно, написанное студентом, то в любом случае станет лучше. Даже если получится такое же говно по формальных характеристиками, но это будет уже свое говно.

.>>>Всё говно. Тогда какая разница как писать? Пиши с DI.
G>>Можно писать не говно, с наличием\отсуствием DI корреляция слабая.
.>Не знаю, по моему опыту корреляция есть, хотя о наличии причино-следственной связи я тоже сомневаюсь. Причина наверное компетентность команд.
Ну между IQ и длиной шнурков тоже есть корреляция слабая

G>>Хороший BA рисует интерфейс и определяет сценарии пользователя.

.>Хороший дев перерисовывает это один-к-одному в виде BL.
Тогда откуда берутся 100500 сервисов, как ты в примере написал? Или ты не хороший дев?
Re[14]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 23.11.14 14:12
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>>То есть хорошего способа добавления параметра не существует. Так и запишем.

.>>А что есть хороший способ по-твоему?
G>Хороший способ — тот, который позволяет минимизировать площадь изменений.
Как много слов, как много треска.

.>>FCA firm, например. Придёт вот клиент в банк и спросит, почему мой аккаунт деактивирован, я, мол, не смог продать акции вовремя и потерял 100500 денег. Если там ответят "мы не знаем", то можно смело подать в суд и легко выиграть дело, FCA обычно на стороне клиентов.

.>>А если аудит будет содержать слишком много, персональные данные, то можно под DPA 1998 попасть.
G>Как раз пара дополнительных полей в сущности
Вроде очевидно, что действия могут повторяться. Юзера деактивировали, затем активировали, затем деактивировали. Как ты это в два поля упихнёшь? Через запятую?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: DDD, архитектура и т.д.
От: koodeer  
Дата: 23.11.14 14:20
Оценка:
Здравствуйте, IT, Вы писали:

IT>Тест:


IT>
IT>class UserManagerTest
IT>{
IT>    [Test]
IT>    void DeactivateUserTest(long userId)
IT>    {
IT>        using (var db = new MyDB())
IT>        {
IT>            db.Users
IT>                .Where(t => t.UserID == 1)
IT>                .Set  (t => t.IsActive, true)
IT>                .Update();

IT>            new UserManagment().DeactivateUser(1);

IT>            Assert.That(db.Users.First(t => t.UserID == 1), Is.False);
IT>        }
IT>    }
IT>}
IT>


Это мы изменили реальную базу?
Стало быть, ещё нужно её вернуть в исходное состояние посте теста?
Или перед запуском теста(ов) создавать тестовую базу? А как и где указывается, что используется тестовая, а не рабочая?
Re[2]: DDD, архитектура и т.д.
От: koodeer  
Дата: 23.11.14 15:06
Оценка:
Здравствуйте, konsoletyper, Вы писали:

K> Я не знаю, как в .NET, а в Java EE никаких "контекстов" создавать не надо, я просто пишу

@PersistenceContext
private EntityManager em;

K>и в путь.

Когда будет создан этот контекст? При старте приложения, при первом обращении к em или ещё когда? Он будет создаваться каждый раз или один раз до завершения приложения?
Что произойдёт при выбросе исключения? Будет ли нормально закрыто соединение с БД?

И много других вопросов можно задать.
Я полагаю, суть всего этого холивара как раз в этом. Можно что-то указать многословно, но при этом гарантированно обработать все возможные ситуации. А можно гораздо более кратко и понятно, но есть риск, что останется необработанной какая-то исключительная ситуация.
Re[12]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 23.11.14 15:58
Оценка:
Здравствуйте, vmpire, Вы писали:

V>>>Во вторых — где вы нашли утверждение, что это не DI?

V>>>Перечитайте ещё раз мой пост, но теперь внимательно: "И то и другое прекрасно реализуется без DI фреймворков"
.>>Я никогда не слышал о DI фреймворках. Что за зверь такой? Слышал о IoC-контейнерах.
V>Я имел в виду DI framework как частный случай реализации IoC, извините, если запутал.
Ок, теперь выразите свой тезис ещё раз с использованием общеприятой терминологии. Я перестал понимать, что вы пытаетесь доказать.
Мой тезис — для качественного кода типичных программ фреймворк опционален, DI — обязателен.

.>>В моём коде-примере вообще чистая ява, никаких фреймворков, даже классы JDK не использутся, за исключением java.lang.String.

V>Ну так я о том и пишу — не нужно никаких специальных инструментов, для реализации mocks и stubs для юнит тестов.
V>Хотя, конечно, в некоторых случаях они могут сэкономить время.
В смысле mole это не специальный инструмент? Как можно без него замокать "static IQueryable<User> WithoutOpenOrders"?

.>>Я читаю. Вы пишете "Совсем не нужны IoC-и". И вдруг "он[конфигурационный файл] уже есть штатно [в ASP.NET как я понимаю, в c# спеке языка надеюсь такого нет]". А как этот файл работает? Правильно, через IoC-и! Что за каша?

V>Как он внутри работает — для его использования не важно. Но согласен, что DI внутри имеется.
Если хочется писать код осмысленно, а не [а тут происходит магия], то важно знать как он внутри работает.

V>Хотя, если так рассматривать, что без DI вообще ничего не бывает, потому, что внутри любого сколь нибудь большого фреймворке DI хоть в каком-то виде есть.

Бывает жуткий SL, new или тупо статики.

V>Но мы ведь, надеюсь, рассматриваем случаи явного применения DI

А что значит "неявное применение DI"?

.>>Для справки: DI — способ реализации IoC.

V>Да, поэтому я и написал "DI framework", чтобы более точно обрисовать, о чём я говорю. IoC — более широкое понятие, но я сейчас только от DI.
Для DI фреймворк не нужен, тесты, например, можно и без фреймворков писать, просто упрощает описание wiring.

V> ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);

Вот эта лажа Shim, Get — жуть.

.>>Всё, никаких xml/dll/кодогенерации и прочего треша.

V>Единственное, что нужно дополнительно — это ткнуть мышой в сборку, которую нужно мочить и добавиль для неё .fakes файл, который генерируется автоматически и в котором ничего делать не надо.
Во-во. А с powermock ничего не нужно. Просто пишешь обычный java код.

.>>Но суть даже не в этом. Будет ли вы это использовать в продакш коде? Нет! И это плохо, т.к. тесты вашего класса будут особенными, в то время когда идеальные тесты — это простой код, который работает точно так же как главный код.

V>С этим согласен, но тут не MS виноват, а сам подход к тому, что мочатся вызываемые методы, вместо того, чтобы их явно отделить.
Кто-то вот в топике пишет, что static функции это круто, забывая что их тестировать надо.

.>>На джава c DI это ровно одна строчка: "when(userDao.updateUser(user)).thenThrow(new TimeoutException())", пишется за пять секунд. А вам приходится время на это отыскивать.

V>Ну так это не имитация того, что MyDB кидает исключение, это имитация того, что исключение кидает класс userDao.
V>Это и на Moles пишется на ура. И даже без него, если метод updateUser не статический, а виртуальный.
Можно чтобы jdbcOperations.update кидало, в случае с DI — без разницы. А вот в приведённом же выше примере было "new MyDB()...Update", т.е. для создания контекста не используется DI, а создаётся новый контекст, который без moles не протестировать уже. Вот кстати ещё один пример что ваше "любого сколь нибудь большого фреймворке DI хоть в каком-то виде есть." тоже не соответствует действительности. Может и есть, но не там где надо.

.>>Как вручную?

.>>Мой код можно и без фреймворков тестить, но код будет неудобно писать — вместо мокинга методов их придётся переопределять.
V>Да, вручную это не так удобно, особенно если мочить нужно много.
V>Но сам факт, что фреймворки для мокинга нужны часто уже признак того, что код, врозможно, слишком сильно связан.
Не знаю как в .net, в java тест-фреймворки обычно нужны для того, чтобы тесты писались с использованием определённой терминлогии для простоты восприятия. Ну и сокращает boilerplate код. Например, вместо
if(!expected.equals(actual)) throw new AssertError("Expect that " + actual + " equals " + expected);

пишешь
assertEquals(expected, actual);


V>>>Я не вижу требований по использованию EF, не вижу требований к деплойменту. Я даже не вижу требования использовать в продукте .NET, если на то пошло.

.>>Требования "использовать EF/.net"? Это кто такие требования выдвигает? MS?
.>>Не важно, пусть вы архитектор. Принимайте сами эти решения, это я буду БА — требования с т.з. бизнеса я уже писал, поищите в треде.
V>Ну так я выдвину требование: сделать всё максимально просто.
Согласен. Делайте максимально просто, но выполнив все бизнес-требования которые были в моём коде! Их напомнить? Или сами поищете в топике?

V>И напишу одну строчку

V>
V>public SetUserInactive()
V>{
V>    using(var con = new SqlDbConnection("строка коннекта"))
V>    {
V>        con.Open();
V>        new SqlDbbCommand("UPDATE User SET IsActive = 0 WHERE UserId = 10").ExecuteNoQuery();
V>    }
V>}
V>

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

V>Если нужна трёхзвенка (хотя, я такого требования не видел), то приписать сверху атрибут [WebMethod] и захостить в каком-нибудь процессе.

Ок, это за рамки архитектуры выходит. Тут всё тривиально, можно не рассматривать.

V>>>И что мешает это сделать на ADO.NET? Зачем тут вообще все эти навороты

.>>Сделайте. Мы посмотрим.
V>Выше
Добавьте реализацию остальных бизнес-требований, продолжим.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[22]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 23.11.14 17:48
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>>В том то и дело, что я много делал проектов, где "много всего". Если на каждую мелочь плодить классы-интерфейсы-фабрики-адаптеры, то сложность поддержки растет очень быстро, даже если каждый кусочек из "много чего" довольно прост.

.>>Альтернативы-то какие? Писать один методы по 500 строк и забивать на тесты?
G>Уже выше написал: чисты функции, детерминированные функции.
Покажи класс.

.>>>>Простую вещь можно сделать сложно, но это не значит что не бывает сложных вещей.

G>>>И что? Разговор то как раз про простые вещи, а ты без необходимости усложняешь. Собственно много кто так делает.
.>>Что значит без необходимости?
G>То и значит. Ты сам привел код в несколько раз сложнее, чем необходимо для решения задачи, оправдав это какимто неведомым функционалом.
Я показал и простой код в одну строчку (проще твоего, кстати). А вот ты сложный код не показывал. А простому коду и архитектура не нужна. Нечего и обсуждать в этом форуме.

G>Кто гарантирует, что также не произойдет в реальном проекте?

А кто гарантирует что ты не убьёшь котёнка, запивая кровью младенца?

.>>Топик о том, зачем нужно это всё. Конечно, если у тебя простое требование — обновить одно поле в БД, то и код будет простым. Скучно, обсуждать нечего. Я придумал несколько новых требований, которые однострончым кодом уже не выразить и показал как разбиение на слои позволяет получить хороший код, даже в случае сложных вещей.

G>Ага, также из-за того, что "скучно, обсуждать нечего" ты поступишь и в реальном проекте.
И убью котёнка, запив кровью младенца.

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

.>>Так покажи как надо. Но не один апдейт, а все требования которые я уже неоднократно описывал и реализованы в моём коде.
G>Я тебе показал как надо.
Ты показал тривиальный код, который делает ровно одно действие. Я показал его аналог. Но вопрос в том, что делать когда действий много и в одну стоку они не влазят.

G>И про атрибуты объяснил. Но ты до сих пор не веришь, что можно написать просто.

Не надо мне объяснений. Код покажи, сотый раз прошу. Пока нет кода — не поверю.

.>>>>Обычно точно после 10-15 человек. Когда одна команда становится слишком большой, придётся делить на несколько, и для уменьшения лагов желательно чтобы команды работали как можно более независимо.

G>>>Чтобы команды были независимыми надо не слои делать, а фичи. Каждая команда пилит свою фичу снизу до верху. Фичи можно сделать независимыми, слои — почти никогда.
.>>А ещё фичи между собой должны взаимодействовать.
G>В том и прикол фич, что не должны. Фичи — отделяемая часть функционала, имеющая ценность для пользователя сама по себе. Если фичи функционально связаны, то он не пилятся параллельно.
А как они связаны? Слова-слова, опять слова.

.>>>>"кто" я уже показывал — будет просто деталью имплементации AuditService, т.е. менять ничего не потребуется, просто добавить деталь.

.>>>>"Причина" — не знаю. Можно больше подробностей? Что там может быть и откуда браться?
G>>>Еще раз — надо не в аудит, а в поле пользователя. У тебя нет хорошего решения такой проблемы. А такие проблемы на два порядка чаще воникают, чем вообще появляется необходимость аудита.
.>>Не понял я. В смысле добавить параметр в deactivateUser? Напугал ежа. Да пожалуйста, одна строчка добавится, ну и один тест...
G>Вранье, у тебя три "слоя" и, соответственно, три передачи параметров. Это значит как минимум в трех местах поменять.
У меня только первый слой содержит голый параметр, в другие слои передаётся User, через него и параметры можно протаскивать.

G>>>Можно не чистые а детерминированные, чтобы поведение зависело только от параметров.

.>>В каком месте у меня в коде недерминированность?
G>Во всех методах классов. Каждый метод класса зависит не только от параметров но и от состояния объекта.
Верно, но с учётом того, что все мои классы stateless, то получится "Каждый метод класса зависит только от параметров".

G>>>Главное уменьшить количество уровней косвенности и связей через состояние.

.>>Код в студию, или не было.
G>Так ты сам приводил код. У тебя там как минимум три уровня косвенности. И это на примитивной задаче.
Приведи _свой_ код.

.>>>>>>Зависимость не от мнения, а от требований. Если мы вдруг мышек должны переделать в ёжиков, то мудрого стратегического совета не хватит, надо тупо будет переделывать. Если что-то маленькое, то и изменение будет маленькое. Масштаб изменения сооизмерим с масштабом нового требования.

G>>>А ты заранее масштабы новых требований знаешь? Я сомневаюсь. Поэтому уменьшать площадь изменений надо уменьшать независимо от мнения.
.>>Код в студию, или не было.
G>Ты сам приводил код
Я прошу привести твой правильный код, чтобы было с чем сравнивать.

G>>>Ага, только когда надо его добавить в 10 функций или в одну — разница огромная. А потом еще тесты поправить.

.>>Если и правда надо в десять, но можно выбрать другой подход. Хотя даже и добавление параметра занимает ~3 секунды. Будет тридцать секунд, невелика потеря. И вообще: Код в студию, или не было.
G>Да не занимает оно три секунды. От повторения это правдой не станет. Тебе для начала надо разбораться куда надо добавить и как у тебя control flow и data flow выглядит перед тем как добавлять. А потом еще ошибки будут, тесты править. В лучшем случае полдня в приложении на 10к+ строк.
_Покажи_ (не расскажи) способ лучше.

.>>>>Я стал спорить с тем, что фреймворк обязателен. Практически обязательным является DI паттерн, ибо он хорошо вписывается куда угодно. В моём примере никаких фреймворков нет.

G>>>Ты написал пример с кучей слоев. Я тебе показал, что фреймворк закрывает потребности и куча слоев не нужна. Ты начал спорить.
.>>Я показал что твой однострочный код с фреймворком эквивалентен однострочному коду без фреймворка, но сложнее тестировать. Код эквивалентный по фичам моему ты не показывал.
G>Тебе еще раз объяснить про атрибуты? Мне лениво писать более трех строк в форуме, особенно если код не решает какую-то проблему.
Не надо мне объяснять. Просто покажи код. Как так лениво? Я не понимаю, ты говорил, что все эти требования примитивны, выражаются одой строчкой и парой атрибутов. Откуда вдруг более трёх взялось?

G>>>Прочитай еще раз первое сообщение в теме. Там человек как раз говорит о бесполезности паттернов с современными фреймворками.

.>>Он спрашивал зачем слои нужны. Я ответил.
G>Ага, придумал какие-то сложности и геройски их победил (хотя еще не факт что победил). Так и в реальных приложениях поступают, от этого пост и родился.
Я не придумал сложности, я придумал business requirements, чтобы задачу имело смысл рассматривать в форуме "Архитектура". Если бы человек не знал как проапдейтить поле базы, но бы пошел в форум "SQL" или "C#".

G>>>С чего это? Чем явный вызов лучше атрибута?

.>>У метода могут быть аргументы, тут же вычисленные, можно возвращаемое значение, обработку исключений и т.п.
G>Аргументы откуда берутся? Видимо из запроса, больше не от куда. А в атрибуте есть контекст запроса, из которого те же параметры можно вытащить.
Вычисляются кодом внутри метода. Потенциально — не только из запроса, но и из БД, настроек конфигов, сторонних сервисов и бог весть чего.

.>>И тестировать проще.

G>Вранье. Спишем просто на незнание.
Атрибуты обычно только integration tests можно протестить, простой код — можно тестить с unit tests.

.>>Атрибут лишь хорош, если тебя в большинстве методов надо делать ровно одно и то же.

G>Так и есть. Мы ведь говорили об аудите, это разве не надо делать в большинстве методов?
Не путаем аудит с логгированием. Нет, не надо во всех, а только в тех методах, которые делают business sensitive operations.

G>>>Атрибут — класс. Только он неинтрузивно работает. Вызывающий код не знает об атрибутах, атрибут не знает на какой класс он навешан. Фактически связь обеспечивает фреймворк. Меньше связей — меньше изменений. Правда можно так понасоздавать неявных связей, что еще хуже, чем явные

.>>То же и интерфейс. Атрибут для АОП хорош, но это не означает что везде их пихать надо.
G>А я не предлагаю везде, я предлагаю заменить кучи вызовов одинакового функционала на атрибуты. Например проверки, аудиты итп. А после вытаскивания такого cross-cutting функционала становятся не нужны слои, достаточно пары функций в контроллере.
Код в студию.

G>>>http://stackoverflow.com/questions/8508190/how-do-i-unit-test-a-custom-actionfilter-in-asp-net-mvc

.>>Это вроде тест фильтра, который повешен на атрибут, а не наличие и корректность атрибута.
G>А тебе надо тестировать наличие атрибута? Совсем двинулся? Посмотреть нельзя?
Как протестить что аудит случается только если активный юзер деактивируется? Т.е. деактивация деактивированного юзера не создаёт запись аудита.

G>>>>>Да, и это прекрасно. Лучше 10 атрибутов, чем 10 ссылок на другие объекты.

.>>>>В чём лучше?
G>>>В том, что меньше связей.
.>>Код в студию, или не было.
G>Код чего? Ты сам class coupling не можешь померить?
Ты говоришь "меньше". Меньше чем что? Померить в твоём воображаемом коде? Нет, извини, не могу.

G>>>>>И чего в ней такого убогого? Твой код и так зависит от фреймворка, еще одна зависимость от контекста ситуацию не изменит. Если ты сам пишешь код, то лучше DI, чем DL — связей меньше.

.>>>>Ты код хоть читал? Покажи строчку где там фреймворк?? Это чистый ява код.
G>>>Ты же сам нашел где-то DL в фреймворке и начал ругать.
.>>Что-то я упустил. Что за DL? Ты имеешь в виду SL?
G>Без разницы, хоть DL, хоть SL, хоть ЖL, суть не меняется.
Я надеялся, что появится суть в твоём ответе. А раз она не менятеся, то там её видимо нет. Понятно.

G>>>Ты пробовал реально делить слои между командами? Результат ужасен. Можно еще отделить серверный код от клиентского, согласовав между ними интерфейс. Если делить всю разработку по слоям, то скорость внесения изменений упадет до нуля. Особенно при кейсах когда надо поле добавить. Так что тут тоже слои мимо кассы.

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

G>>>Пойми уже наконец, что никакой потребности деления на слои не существует, кроме уменьшения площади изменений. Если этого достигнуть не удается, то и не надо заниматься порнографией.

.>>Код в студию, или не было.
G>Оо код чего? Ты же сам привел пример, который очень тяжело поддается изменениям, типа добавить колонку. Зачем тебе еще примеры?
Мне нужен пример эквивалентного по фичам кода, который поддаётся изменениям легче.

.>>А зачем воспроизводить поведение в точности? unit-тесты обычно whitebox тесты. Т.е. ты знаешь на какое поведение рассчитывает твой код, так и мочишь.

G>Твое знание должно в этом случае совпадать с поведением фреймворка, иначе баги.
Естественно. Альтернативы?

G>Яркий пример. Ты пишешь код, который ориентируется что компонент отдает даты в локали пользователя. Пишешь тест, основанный на таком поведении. А по факту он отдает в UTC, получаешь багу.

А откуда я это взял, что "отдает даты в локали пользователя"? Если я это прочитал в доке, никуда не денусь, на багу придётся напороться во время интеграционного теста, поправить юнит тест, сделав его красным, затем исправить код, сделав тест зелёным. Если я это придумал сам, то сам и виноват. Но сценарий тот же.

.>>Набор юнит-тестов показывает какая именно логика реализована и какая будет реакция.

G>Если ты не повторяешь в точности поведение зависимых компонентов, то вообще ничего не показывает.
Показывает _ожидаемое_ поведение. Скажем, типичный HttpRequest может быть иметь 100500 различных состояний, там и куча параметров, атрибутов, куки, сессии, разные версии протокола. Это не означает, что тесты невозможны. Просто твой код будет реагировать на некое вполне определённое подмножество состояний, которые легко описывать в виде моков.

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

G>Я же приводил кейс, читай внимательно. У тебя обработчик события на загрузку файла в storage. В обработчик приходит набор параметров. Сформировать набор параметров для моков по сложности равно разработке storage. Как в этой ситуации unit-тестировать?
unit-тест будет не сложнее тестируемого кода. Раз у тебя этот обработчик события написан на ЯП, то и код теста ты можешь написать.

.>>Куда потребуется, туда и добавлю, может лучше в securityService подойдёт, я не понимаю точно твоё требование.

G>Да все ты понимаешь, и понимаешь, что такой простой кейс начинает проникать в кучу классов, увеличивая сложность поддержки.
Увеличивая по сравнении с чем?

.>>А ты что предлагаешь? Навешать на все методы магический атрибут?

G>Да, по науке это называется AOP. Когда такой cross-cutting concern не пишется явно в коде, а инжектится извне, уменьшая связность и площадь изменений.
AOP это тоже архитектурное решение, ничем не луче и ничем не хуже чем DAO/BL/etc. Просто инструмент, который надо использовать по делу. Честно говоря, в начальном вопросе мне не удаётся усмотреть необходимость применения AOP. Или ты этой пушкой стреляешь по всем воробьям?

.>>>>У функций будет десятки аргументов...

G>>>Значит слишком толстая функция получилась.
.>>И что делать?
G>Побить на маленькие.
Пример в студию. Особенно интересно как навешивать атрибуты на чистые статические функции.

G>>>Хороший BA рисует интерфейс и определяет сценарии пользователя.

.>>Хороший дев перерисовывает это один-к-одному в виде BL.
G>Тогда откуда берутся 100500 сервисов, как ты в примере написал? Или ты не хороший дев?
UserService и есть BL. Но помимо BL есть и другие слои.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: DDD, архитектура и т.д.
От: koodeer  
Дата: 23.11.14 18:43
Оценка:
Здравствуйте, ., Вы писали:

.>надо писать

class UserService
{
  @PersistenceContext private EntityManager em;

  void deactivateUser()
  {
       em.executeQuery(...);
  }
}

.>Вот этот элементарный приём позволяет сильно всё упростить.

.>А можно кратко и понятно, обрабатывая исключительные ситуации.


Тогда почему выше по топику ты пишешь длинно и непонятно? Оппоненты как раз и приводят код в одну строчку, а весь остальной контекст передаётся через атрибуты (аннотации).
Re[5]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 23.11.14 21:37
Оценка:
Здравствуйте, koodeer, Вы писали:

.>>надо писать

K>
class UserService
K>{
K>  @PersistenceContext private EntityManager em;

K>  void deactivateUser()
K>  {
K>       em.executeQuery(...);
K>  }
K>}

.>>Вот этот элементарный приём позволяет сильно всё упростить.

.>>А можно кратко и понятно, обрабатывая исключительные ситуации.


K>Тогда почему выше по топику ты пишешь длинно и непонятно? Оппоненты как раз и приводят код в одну строчку, а весь остальной контекст передаётся через атрибуты (аннотации).

Кстати, забыл добавить, что этот код выше элементарно при желании переделывается в чистый код (используя кодогенерацию в IDE), т.е. без атрибутов|аннотаций.
class UserService
{
  private final EntityManager em;
  public UserService(EntityManager em)
  {
    this.em = em;
  }

  void deactivateUser()
  {
       em.executeQuery(...);
  }
}

Тогда и не нужны фреймворки, которые магически (неявно) обрабатывают всякие @PersistenceContext. Т.е. передача через атрибуты|аннотации это лишь деталь имплементации, фича ЯП немного уменьшающая количество boilerplate кода.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: DDD, архитектура и т.д.
От: IT Россия linq2db.com
Дата: 24.11.14 03:54
Оценка:
Здравствуйте, koodeer, Вы писали:

K>Это мы изменили реальную базу?


А почему этот вопрос ко мне, а не к тому кто предложил подобный код для обсуждения?
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: DDD, архитектура и т.д.
От: konsoletyper Россия https://github.com/konsoletyper
Дата: 24.11.14 07:42
Оценка:
Здравствуйте, koodeer, Вы писали:

K>> Я не знаю, как в .NET, а в Java EE никаких "контекстов" создавать не надо, я просто пишу

K>
@PersistenceContext
K>private EntityManager em;

K>>и в путь.

K>Когда будет создан этот контекст? При старте приложения, при первом обращении к em или ещё когда? Он будет создаваться каждый раз или один раз до завершения приложения?

K>Что произойдёт при выбросе исключения? Будет ли нормально закрыто соединение с БД?

В случае Spring этот контекст — экземпляр SharedEntityManagerBean, который вообще-то синглтон, но внутри себя он делегирует все вызовы к ThreadLocal<EntityManager>, который в свою очередь привязывается к текущему треду в зависимости от настроек. В типичном веб-приложении, EM будет создаваться с помощью PlatformTransactionManager, а конкретно при декларативном описании транзакций — при вызове метода, маркированного @Transactional (соответственно, фиксироваться она будет по завершению работы метода). Как правило, @Transactional я ставлю на классах-фасадах, которые отделяют бизнес-логику от внешнего мира, и которые я вызываю из веб-контроллеров, или с клиента через GWT-RPC.

При выбросе исключения в зависимости от настроек транзакция будет либо откатана, либо зафиксирована. Кстати, можно настроить и так, чтобы транзакция в любом случае откатывалась. Соединение в БД будет не закрываться, а возвращаться в пул. Конечно же, вызовы всего и вся проходят через код spring, который уже умеет корректно отрабатывать все ситуации. Если чем-то не устраивает стандартное поведение, никто не мешает сделать всё вручную, см. выше.

K>Я полагаю, суть всего этого холивара как раз в этом. Можно что-то указать многословно, но при этом гарантированно обработать все возможные ситуации. А можно гораздо более кратко и понятно, но есть риск, что останется необработанной какая-то исключительная ситуация.


В данном случае не вижу, какая ситуация не будет обработана. От использования декларативного управления транзакциями можно отказаться в конкретном методе и пользоваться TransactionTeamplate/PlatformTransactionManager. Можно хоть инжектить EntityManagerFactory и вручную создавать EM, стартовать и фиксировать транзакцию.
Re[6]: DDD, архитектура и т.д.
От: konsoletyper Россия https://github.com/konsoletyper
Дата: 24.11.14 07:53
Оценка:
Здравствуйте, ., Вы писали:

.>Кстати, забыл добавить, что этот код выше элементарно при желании переделывается в чистый код (используя кодогенерацию в IDE), т.е. без атрибутов|аннотаций.

.>Тогда и не нужны фреймворки, которые магически (неявно) обрабатывают всякие @PersistenceContext. Т.е. передача через атрибуты|аннотации это лишь деталь имплементации, фича ЯП немного уменьшающая количество boilerplate кода.

Ну так и с аннотациями код остаётся чистым. По сути аннотации ничего не делают, а просто присутствуют в виде метаинформации. Не факт, что они вообще будут обработаны каким-либо контейнером (те же тесты бизнес-логики я имею привычку писать вообще без IoC-контенера).
Re[13]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 24.11.14 10:57
Оценка:
Здравствуйте, ., Вы писали:

.>Ок, теперь выразите свой тезис ещё раз с использованием общеприятой терминологии. Я перестал понимать, что вы пытаетесь доказать.

.>Мой тезис — для качественного кода типичных программ фреймворк опционален, DI — обязателен.
Как раз то, что фреймворк — опционален

.>>>В моём коде-примере вообще чистая ява, никаких фреймворков, даже классы JDK не использутся, за исключением java.lang.String.

V>>Ну так я о том и пишу — не нужно никаких специальных инструментов, для реализации mocks и stubs для юнит тестов.
V>>Хотя, конечно, в некоторых случаях они могут сэкономить время.
.>В смысле mole это не специальный инструмент? Как можно без него замокать "static IQueryable<User> WithoutOpenOrders"?
Это специальный инструмент. Так же, как и PowerMock.
Static без него можно замокать только если он написан так, что явно предусматривает возможность моков.

.>>>Я читаю. Вы пишете "Совсем не нужны IoC-и". И вдруг "он[конфигурационный файл] уже есть штатно [в ASP.NET как я понимаю, в c# спеке языка надеюсь такого нет]". А как этот файл работает? Правильно, через IoC-и! Что за каша?

V>>Как он внутри работает — для его использования не важно. Но согласен, что DI внутри имеется.
.>Если хочется писать код осмысленно, а не [а тут происходит магия], то важно знать как он внутри работает.
Тогда уж нужно до ассемблера всё понимать, или даже до электрических сигналов (кстати, этот уровень позволяет понять причину некоторых зависаний).
На практике где-то нужно остановиться. Впрочем, это уже совсем другая философская тема.

V>>Хотя, если так рассматривать, что без DI вообще ничего не бывает, потому, что внутри любого сколь нибудь большого фреймворке DI хоть в каком-то виде есть.

.>Бывает жуткий SL, new или тупо статики.
Ну это совсем в запущенных случаях только. Или совсем в простых.

V>>Но мы ведь, надеюсь, рассматриваем случаи явного применения DI

.>А что значит "неявное применение DI"?
Которое внутри фреймворка и при использовании наружу не выходит. Например, в .NET парсинг секций .config файла. Оно внутри само подставляет нужные классы для каждой секции, но снаружи это не видно — просто получаешь сразу типизированный результат.

.>>>Для справки: DI — способ реализации IoC.

V>>Да, поэтому я и написал "DI framework", чтобы более точно обрисовать, о чём я говорю. IoC — более широкое понятие, но я сейчас только от DI.
.>Для DI фреймворк не нужен, тесты, например, можно и без фреймворков писать, просто упрощает описание wiring.

V>> ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);

.>Вот эта лажа Shim, Get — жуть.
Это вкусовщина. Мне вот не нравится значок @ в атрибутах на джаве.

.>>>Всё, никаких xml/dll/кодогенерации и прочего треша.

V>>Единственное, что нужно дополнительно — это ткнуть мышой в сборку, которую нужно мочить и добавиль для неё .fakes файл, который генерируется автоматически и в котором ничего делать не надо.
.>Во-во. А с powermock ничего не нужно. Просто пишешь обычный java код.
да, согласен, это экономит целых секунд пять.

.>Кто-то вот в топике пишет, что static функции это круто, забывая что их тестировать надо.

Я про крутость static функций не писал. По крайней мере, в этом треде.

.>>>Как вручную?

.>>>Мой код можно и без фреймворков тестить, но код будет неудобно писать — вместо мокинга методов их придётся переопределять.
V>>Да, вручную это не так удобно, особенно если мочить нужно много.
V>>Но сам факт, что фреймворки для мокинга нужны часто уже признак того, что код, врозможно, слишком сильно связан.
.>Не знаю как в .net, в java тест-фреймворки обычно нужны для того, чтобы тесты писались с использованием определённой терминлогии для простоты восприятия. Ну и сокращает boilerplate код. Например, вместо
.>
.>if(!expected.equals(actual)) throw new AssertError("Expect that " + actual + " equals " + expected);
.>

.>пишешь
.>
.>assertEquals(expected, actual);
.>

Не вижу, как это относится к тебе обсуждения, но в .NET это выглядит почти так же:
Assert.Equal(expected, actual);


V>>>>Я не вижу требований по использованию EF, не вижу требований к деплойменту. Я даже не вижу требования использовать в продукте .NET, если на то пошло.

.>>>Требования "использовать EF/.net"? Это кто такие требования выдвигает? MS?
Вы проглядели предлог "не"

.>>>Не важно, пусть вы архитектор. Принимайте сами эти решения, это я буду БА — требования с т.з. бизнеса я уже писал, поищите в треде.

V>>Ну так я выдвину требование: сделать всё максимально просто.
.>Согласен. Делайте максимально просто, но выполнив все бизнес-требования которые были в моём коде! Их напомнить? Или сами поищете в топике?
Я не очень понимаю, чего Вы хотите добиться. Я код полноценного приложения в форуме писать всё равно не буду, тем более на джаве, которую не знаю.
Всё, что я утверждал — это то, что для реализации mocks и stubs специальные фреймворки не являются необходимыми.
Но если вы не знаете, как реализовать ваши бизнес требования — перечислите их, попробую помочь.

V>>И напишу одну строчку

V>>
V>>public SetUserInactive()
V>>{
V>>    using(var con = new SqlDbConnection("строка коннекта"))
V>>    {
V>>        con.Open();
V>>        new SqlDbbCommand("UPDATE User SET IsActive = 0 WHERE UserId = 10").ExecuteNoQuery();
V>>    }
V>>}
V>>

.>Давайте теперь тест показывайте, но только как вы утверждаете чтобы "не нужно никаких специальных инструментов, для реализации mocks и stubs для юнит тестов."
Тест на что? Что запрос послался в базу?
Или что в базе что-то изменилось?
Re[23]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.11.14 12:57
Оценка:
Здравствуйте, ., Вы писали:

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


G>>>>В том то и дело, что я много делал проектов, где "много всего". Если на каждую мелочь плодить классы-интерфейсы-фабрики-адаптеры, то сложность поддержки растет очень быстро, даже если каждый кусочек из "много чего" довольно прост.

.>>>Альтернативы-то какие? Писать один методы по 500 строк и забивать на тесты?
G>>Уже выше написал: чисты функции, детерминированные функции.
.>Покажи класс.
Зачем? Ты все равно не поверишь.

.>>>>>Простую вещь можно сделать сложно, но это не значит что не бывает сложных вещей.

G>>>>И что? Разговор то как раз про простые вещи, а ты без необходимости усложняешь. Собственно много кто так делает.
.>>>Что значит без необходимости?
G>>То и значит. Ты сам привел код в несколько раз сложнее, чем необходимо для решения задачи, оправдав это какимто неведомым функционалом.
.>Я показал и простой код в одну строчку (проще твоего, кстати). А вот ты сложный код не показывал. А простому коду и архитектура не нужна. Нечего и обсуждать в этом форуме.
Правильно, поэтому упрощай код любыми доступными средствами.


G>>И про атрибуты объяснил. Но ты до сих пор не веришь, что можно написать просто.

.>Не надо мне объяснений. Код покажи, сотый раз прошу. Пока нет кода — не поверю.
Я показал уже, ты не веришь. Зачем еще что-то писать?

.>У меня только первый слой содержит голый параметр, в другие слои передаётся User, через него и параметры можно протаскивать.

Тогда зачем тебе слои? Если они у тебя только pass-through? Чтобы вызывать аудиты всякие? Тогда уж лучше атрибуты и АОП.

G>>>>Можно не чистые а детерминированные, чтобы поведение зависело только от параметров.

.>>>В каком месте у меня в коде недерминированность?
G>>Во всех методах классов. Каждый метод класса зависит не только от параметров но и от состояния объекта.
.>Верно, но с учётом того, что все мои классы stateless, то получится "Каждый метод класса зависит только от параметров".
Это вранье, классы получают ссылки на другие классы, которые в общем случае не stateless. Ну или ты не узнаешь об этом пока все не прочитаешь. Состояние транзитивно. Достаточно в одном классе поиметь состояние, кк все зависящие от него также получают это состояние.

G>>>>Главное уменьшить количество уровней косвенности и связей через состояние.

.>>>Код в студию, или не было.
G>>Так ты сам приводил код. У тебя там как минимум три уровня косвенности. И это на примитивной задаче.
.>Приведи _свой_ код.
А я тебе привел, один метод, этого более чем дотаточно. Нужно будет аудит — накручивается через атрибуты.
http://rionscode.wordpress.com/2013/03/03/implementing-audit-trails-using-asp-net-mvc-actionfilters/
Вот пример как аудит атрибутами накрутить.

.>>>>>>>Зависимость не от мнения, а от требований. Если мы вдруг мышек должны переделать в ёжиков, то мудрого стратегического совета не хватит, надо тупо будет переделывать. Если что-то маленькое, то и изменение будет маленькое. Масштаб изменения сооизмерим с масштабом нового требования.

G>>>>А ты заранее масштабы новых требований знаешь? Я сомневаюсь. Поэтому уменьшать площадь изменений надо уменьшать независимо от мнения.
.>>>Код в студию, или не было.
G>>Ты сам приводил код
.>Я прошу привести твой правильный код, чтобы было с чем сравнивать.
Я тебе привел правильный код. Чем он неправильный?

G>>>>Ага, только когда надо его добавить в 10 функций или в одну — разница огромная. А потом еще тесты поправить.

.>>>Если и правда надо в десять, но можно выбрать другой подход. Хотя даже и добавление параметра занимает ~3 секунды. Будет тридцать секунд, невелика потеря. И вообще: Код в студию, или не было.
G>>Да не занимает оно три секунды. От повторения это правдой не станет. Тебе для начала надо разбораться куда надо добавить и как у тебя control flow и data flow выглядит перед тем как добавлять. А потом еще ошибки будут, тесты править. В лучшем случае полдня в приложении на 10к+ строк.
.>_Покажи_ (не расскажи) способ лучше.
Я уже показал. Для примитивного функционала достаточно трех строк.

.>>>>>Я стал спорить с тем, что фреймворк обязателен. Практически обязательным является DI паттерн, ибо он хорошо вписывается куда угодно. В моём примере никаких фреймворков нет.

G>>>>Ты написал пример с кучей слоев. Я тебе показал, что фреймворк закрывает потребности и куча слоев не нужна. Ты начал спорить.
.>>>Я показал что твой однострочный код с фреймворком эквивалентен однострочному коду без фреймворка, но сложнее тестировать. Код эквивалентный по фичам моему ты не показывал.
G>>Тебе еще раз объяснить про атрибуты? Мне лениво писать более трех строк в форуме, особенно если код не решает какую-то проблему.
.>Не надо мне объяснять. Просто покажи код. Как так лениво? Я не понимаю, ты говорил, что все эти требования примитивны, выражаются одой строчкой и парой атрибутов. Откуда вдруг более трёх взялось?
Потому что сама реализация атрибутов гораздо больше, чем три строки. Ты же не привел реализацию аудита и проверок. Я с тем же успехом просто добавил атрибут на метод. Я тебе уже приводил этот код:
    [Action("Deactivate")]
    [HttpPost]
    [Audit] //Добавил
    [SomeChecks] //Добавил
    async ActionResult DeactivatePost(int userId)
    {
        var user = await ctx.Users
                      .WithoutOrders()
                      .FirstAsync(u => u.Id == userId);
        user.Actve = false;
        await ctx.SaveChagesAsync();
        return Redirect("Index");
    }

Вот тебе код, который логически эквивалентен твоему.
Еще раз привожу ссылку на аудит с помощью атрибутов — http://rionscode.wordpress.com/2013/03/03/implementing-audit-trails-using-asp-net-mvc-actionfilters/ чтобы ты понял.


G>>>>С чего это? Чем явный вызов лучше атрибута?

.>>>У метода могут быть аргументы, тут же вычисленные, можно возвращаемое значение, обработку исключений и т.п.
G>>Аргументы откуда берутся? Видимо из запроса, больше не от куда. А в атрибуте есть контекст запроса, из которого те же параметры можно вытащить.
.>Вычисляются кодом внутри метода. Потенциально — не только из запроса, но и из БД, настроек конфигов, сторонних сервисов и бог весть чего.
А что мешает тоже самое сделать в атрибутах?


.>>>И тестировать проще.

G>>Вранье. Спишем просто на незнание.
.>Атрибуты обычно только integration tests можно протестить, простой код — можно тестить с unit tests.
А что ты юнит-тестами проверить можешь? Только то, что вызывается аудит при выполнении метода? Так с атрибутами это без тестов проверить можно. А то, что удит правильно пишется, ты только интеграционным тестированием и покроешь.
Вообще полезность юнит тестов при такой примитивной логике нулевая, а по факту даже отрицательная, ибо их еще поддерживать надо.

.>>>Атрибут лишь хорош, если тебя в большинстве методов надо делать ровно одно и то же.

G>>Так и есть. Мы ведь говорили об аудите, это разве не надо делать в большинстве методов?
.>Не путаем аудит с логгированием. Нет, не надо во всех, а только в тех методах, которые делают business sensitive operations.
То есть в 90%, какая разница?

G>>>>Атрибут — класс. Только он неинтрузивно работает. Вызывающий код не знает об атрибутах, атрибут не знает на какой класс он навешан. Фактически связь обеспечивает фреймворк. Меньше связей — меньше изменений. Правда можно так понасоздавать неявных связей, что еще хуже, чем явные

.>>>То же и интерфейс. Атрибут для АОП хорош, но это не означает что везде их пихать надо.
G>>А я не предлагаю везде, я предлагаю заменить кучи вызовов одинакового функционала на атрибуты. Например проверки, аудиты итп. А после вытаскивания такого cross-cutting функционала становятся не нужны слои, достаточно пары функций в контроллере.
.>Код в студию.
См выше.

G>>>>http://stackoverflow.com/questions/8508190/how-do-i-unit-test-a-custom-actionfilter-in-asp-net-mvc

.>>>Это вроде тест фильтра, который повешен на атрибут, а не наличие и корректность атрибута.
G>>А тебе надо тестировать наличие атрибута? Совсем двинулся? Посмотреть нельзя?
.>Как протестить что аудит случается только если активный юзер деактивируется? Т.е. деактивация деактивированного юзера не создаёт запись аудита.
Просто не показывать UI пользователю с возможностью деактивировать уже деактивированного юзера. Ты не на том уровне пытаешься проблему решать.
Ты понимаешь, что такая логика, как у тебя, в реальном приложении приведет к тонне говнокода?

G>>>>>>Да, и это прекрасно. Лучше 10 атрибутов, чем 10 ссылок на другие объекты.

.>>>>>В чём лучше?
G>>>>В том, что меньше связей.
.>>>Код в студию, или не было.
G>>Код чего? Ты сам class coupling не можешь померить?
.>Ты говоришь "меньше". Меньше чем что? Померить в твоём воображаемом коде? Нет, извини, не могу.
Я выше код привел — измеряй.


G>>>>Пойми уже наконец, что никакой потребности деления на слои не существует, кроме уменьшения площади изменений. Если этого достигнуть не удается, то и не надо заниматься порнографией.

.>>>Код в студию, или не было.
G>>Оо код чего? Ты же сам привел пример, который очень тяжело поддается изменениям, типа добавить колонку. Зачем тебе еще примеры?
.>Мне нужен пример эквивалентного по фичам кода, который поддаётся изменениям легче.
См выше, полностью эквивалентный по фичам — вызывается два метода, которые неизвестно что делают.

.>>>А зачем воспроизводить поведение в точности? unit-тесты обычно whitebox тесты. Т.е. ты знаешь на какое поведение рассчитывает твой код, так и мочишь.

G>>Твое знание должно в этом случае совпадать с поведением фреймворка, иначе баги.
.>Естественно. Альтернативы?
Забить на юнит-тесты.

G>>Яркий пример. Ты пишешь код, который ориентируется что компонент отдает даты в локали пользователя. Пишешь тест, основанный на таком поведении. А по факту он отдает в UTC, получаешь багу.

.>А откуда я это взял, что "отдает даты в локали пользователя"? Если я это прочитал в доке, никуда не денусь, на багу придётся напороться во время интеграционного теста, поправить юнит тест, сделав его красным, затем исправить код, сделав тест зелёным. Если я это придумал сам, то сам и виноват. Но сценарий тот же.
У людей есть предел внимательности, такую багу пропустить очень легко. А может и не быть документации по таким мелочам вовсе.
Кстати зачем тебе юнит-тест, который багу не ловит, если у тебя есть интеграционный тест, который ловит?


.>>>Набор юнит-тестов показывает какая именно логика реализована и какая будет реакция.

G>>Если ты не повторяешь в точности поведение зависимых компонентов, то вообще ничего не показывает.
.>Показывает _ожидаемое_ поведение. Скажем, типичный HttpRequest может быть иметь 100500 различных состояний, там и куча параметров, атрибутов, куки, сессии, разные версии протокола. Это не означает, что тесты невозможны. Просто твой код будет реагировать на некое вполне определённое подмножество состояний, которые легко описывать в виде моков.
Ага,а по факту окажется, что в программе совершенно другой набор состояний и тест твой по сути не проверяет ничего.

.>unit-тест будет не сложнее тестируемого кода.

Это зависит от кода, если логика примитивна (поменять поле в базе), то юнит-тест окажется сложнее кода. Просто потому что setup будет занимать примерно половину объема теста.



.>>>Куда потребуется, туда и добавлю, может лучше в securityService подойдёт, я не понимаю точно твоё требование.

G>>Да все ты понимаешь, и понимаешь, что такой простой кейс начинает проникать в кучу классов, увеличивая сложность поддержки.
.>Увеличивая по сравнении с чем?
По сравнению с АОП

.>>>А ты что предлагаешь? Навешать на все методы магический атрибут?

G>>Да, по науке это называется AOP. Когда такой cross-cutting concern не пишется явно в коде, а инжектится извне, уменьшая связность и площадь изменений.
.>AOP это тоже архитектурное решение, ничем не луче и ничем не хуже чем DAO/BL/etc. Просто инструмент, который надо использовать по делу.
Что значит "ничем не лучше"? Для конкретной задачи сильно лучше — снижает class coupling и количество строк. А BL\DAO — наоборот.

.>Честно говоря, в начальном вопросе мне не удаётся усмотреть необходимость применения AOP.

А его там и нет, это ты придумал сложности с BL\DAO и AOP стал оправданным.



.>>>>>У функций будет десятки аргументов...

G>>>>Значит слишком толстая функция получилась.
.>>>И что делать?
G>>Побить на маленькие.
.>Пример в студию. Особенно интересно как навешивать атрибуты на чистые статические функции.
Это ты придумал фукнцию с десятком аргументов. Да и вообще все сложности, которые тут обсуждаем. Так что ты приводи пример, а потом будем думать как его решать.

G>>>>Хороший BA рисует интерфейс и определяет сценарии пользователя.

.>>>Хороший дев перерисовывает это один-к-одному в виде BL.
G>>Тогда откуда берутся 100500 сервисов, как ты в примере написал? Или ты не хороший дев?
.>UserService и есть BL. Но помимо BL есть и другие слои.
Я боюсь представить что изобразил BA, что "один-к-одному в виде BL" получился userservice.
Re[7]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 24.11.14 14:04
Оценка:
Здравствуйте, konsoletyper, Вы писали:

.>>Кстати, забыл добавить, что этот код выше элементарно при желании переделывается в чистый код (используя кодогенерацию в IDE), т.е. без атрибутов|аннотаций.

.>>Тогда и не нужны фреймворки, которые магически (неявно) обрабатывают всякие @PersistenceContext. Т.е. передача через атрибуты|аннотации это лишь деталь имплементации, фича ЯП немного уменьшающая количество boilerplate кода.

K>Ну так и с аннотациями код остаётся чистым. По сути аннотации ничего не делают, а просто присутствуют в виде метаинформации. Не факт, что они вообще будут обработаны каким-либо контейнером (те же тесты бизнес-логики я имею привычку писать вообще без IoC-контенера).

Согласен. Одно мне не нравится с этими аннотациями, что они обычно навешиваются на private поле, а значит без рефлексии нормально не заинжектить, да и по публичному интерфейсу класса невозможно понять какие зависимости у него есть. Более того, если ты добавишь новое private поле для зависимости, то можно нарваться на NPE в рантайм, вместо compile time. Я предпочитаю constructor injection.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: DDD, архитектура и т.д.
От: konsoletyper Россия https://github.com/konsoletyper
Дата: 24.11.14 14:36
Оценка:
Здравствуйте, ., Вы писали:

.>Согласен. Одно мне не нравится с этими аннотациями, что они обычно навешиваются на private поле, а значит без рефлексии нормально не заинжектить, да и по публичному интерфейсу класса невозможно понять какие зависимости у него есть. Более того, если ты добавишь новое private поле для зависимости, то можно нарваться на NPE в рантайм, вместо compile time. Я предпочитаю constructor injection.


За навешивание аннотации на приватное поле и непредоставление возможности это же поле выставить как-либо ещё кроме DI, надо бить по рукам. Хотя бывают иногда места, когда такой шорткат оправдан, например, в интеграционных тестах, которые и так вовлекают конкретный контейнер/фреймворк, или в сильно системных вещах, вроде какой-нибудь кастомной версии BeanFactoryPostProcessor. А через конструктор и я предпочитаю инжектить, но иногда это невозможно. Например, фасад приложения чаще всего требует кучи различных сервисов, а я стараюсь избегать методов с количеством параметров больше 3-4. В таких случаях я делаю инжект через сеттеры.
Re[14]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 24.11.14 14:37
Оценка:
Здравствуйте, vmpire, Вы писали:

.>>Ок, теперь выразите свой тезис ещё раз с использованием общеприятой терминологии. Я перестал понимать, что вы пытаетесь доказать.

.>>Мой тезис — для качественного кода типичных программ фреймворк опционален, DI — обязателен.
V>Как раз то, что фреймворк — опционален

.>>>>В моём коде-примере вообще чистая ява, никаких фреймворков, даже классы JDK не использутся, за исключением java.lang.String.

V>>>Ну так я о том и пишу — не нужно никаких специальных инструментов, для реализации mocks и stubs для юнит тестов.
V>>>Хотя, конечно, в некоторых случаях они могут сэкономить время.
.>>В смысле mole это не специальный инструмент? Как можно без него замокать "static IQueryable<User> WithoutOpenOrders"?
V>Это специальный инструмент. Так же, как и PowerMock.
powermock предполагается использовать исключительно для поддержки legacy code, использовать его при написании нового кода — моветон.

V>Static без него можно замокать только если он написан так, что явно предусматривает возможность моков.

Во-во. Жуть.

V>На практике где-то нужно остановиться. Впрочем, это уже совсем другая философская тема.

Ок.

V>>>Хотя, если так рассматривать, что без DI вообще ничего не бывает, потому, что внутри любого сколь нибудь большого фреймворке DI хоть в каком-то виде есть.

.>>Бывает жуткий SL, new или тупо статики.
V>Ну это совсем в запущенных случаях только. Или совсем в простых.
Почему-то все мои оппоненты приводят в пример запущенный случай. Круто, чё.

V>>>Но мы ведь, надеюсь, рассматриваем случаи явного применения DI

.>>А что значит "неявное применение DI"?
V>Которое внутри фреймворка и при использовании наружу не выходит. Например, в .NET парсинг секций .config файла. Оно внутри само подставляет нужные классы для каждой секции, но снаружи это не видно — просто получаешь сразу типизированный результат.
Не понял. Можно подробнее? Судя по описанию DI тут никоим боком.

V>>> ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);

.>>Вот эта лажа Shim, Get — жуть.
V>Это вкусовщина. Мне вот не нравится значок @ в атрибутах на джаве.
А find usages/rename refactoring/highligh symbol работают так же?

V>>>Единственное, что нужно дополнительно — это ткнуть мышой в сборку, которую нужно мочить и добавиль для неё .fakes файл, который генерируется автоматически и в котором ничего делать не надо.

.>>Во-во. А с powermock ничего не нужно. Просто пишешь обычный java код.
V>да, согласен, это экономит целых секунд пять.
Это вводит лишний мусор в проект. Хотя как я понимаю, дитям майкрософта лишняя верста не крюк, сказали надо, значит надо.

.>>Кто-то вот в топике пишет, что static функции это круто, забывая что их тестировать надо.

V>Я про крутость static функций не писал. По крайней мере, в этом треде.
Возможно не вы, а кто-то другой. Но new в вашем коде это тот же static по сути.

V>Не вижу, как это относится к тебе обсуждения, но в .NET это выглядит почти так же:

V>
V>Assert.Equal(expected, actual);
V>

Просто хотел сказать, что при использовании DI тестовый фреймворк так же опционален. В коде приведённом вами и другими моими оппонентами он был обязателен, т.к. использовались статики или new.

V>>>>>Я не вижу требований по использованию EF, не вижу требований к деплойменту. Я даже не вижу требования использовать в продукте .NET, если на то пошло.

.>>>>Требования "использовать EF/.net"? Это кто такие требования выдвигает? MS?
V>Вы проглядели предлог "не"
Я не понял зачем вам эти требования. Это обычно не то, что даёт БА.

.>>>>Не важно, пусть вы архитектор. Принимайте сами эти решения, это я буду БА — требования с т.з. бизнеса я уже писал, поищите в треде.

V>>>Ну так я выдвину требование: сделать всё максимально просто.
.>>Согласен. Делайте максимально просто, но выполнив все бизнес-требования которые были в моём коде! Их напомнить? Или сами поищете в топике?
V>Я не очень понимаю, чего Вы хотите добиться. Я код полноценного приложения в форуме писать всё равно не буду, тем более на джаве, которую не знаю.
Не надо код полноценного приложения. Ни один программист в реальном проекте не пишет код полноценного приложения. Он реализует конкретные бизнестребования. Вот это я и прошу. Представьте себя на месте программиста, которому поручили реализовать операцию деактивации пользователя. Бизнес-требования таковы:
1. Публичный web-method с параметром userId.
2. Вернуть определённую ошибку если userId не найден в базе
3. Вернуть определённую ошибку если userId соответствует текущему юзеру в сессии и если равен и флаг !mad.
4. Выполнить запрос к securityService, передав userId (этот сервис пишут другие, например, он делает RPC к 3rd party сервису). Воля ваша как определить интерфейс взаимодействия c 3rd party.
5. Записать аудит (вызов 3rd party serivce, передать userId и идентификатор операции аудита "DEACTIVATE_USER"). Аудит не писать если юзер уже был деактивирован.
6. Послать сообщение юзеру (вызов 3rd party serivce). Не посылать сообщение если юзер уже был деактивирован.
7. Собственно сделать запись в БД, что аккаунт деактивирован.
Вы согласны, что примерно такие требования получает типичный программист от типичного БА? (По крайней мере у нас было так)
У меня это заняло около минут 10-15.

V>Всё, что я утверждал — это то, что для реализации mocks и stubs специальные фреймворки не являются необходимыми.

В приведённом ранее вашем коде — являются.

V>Но если вы не знаете, как реализовать ваши бизнес требования — перечислите их, попробую помочь.

Я их реализовал в своём коде. Вы сказали, что я их плохо реализовал. Но не показали как же будет выглядеть хорошая реализация.

V>>>И напишу одну строчку

V>>>
V>>>public SetUserInactive()
V>>>{
V>>>    using(var con = new SqlDbConnection("строка коннекта"))
V>>>    {
V>>>        con.Open();
V>>>        new SqlDbbCommand("UPDATE User SET IsActive = 0 WHERE UserId = 10").ExecuteNoQuery();
V>>>    }
V>>>}
V>>>

.>>Давайте теперь тест показывайте, но только как вы утверждаете чтобы "не нужно никаких специальных инструментов, для реализации mocks и stubs для юнит тестов."
V>Тест на что? Что запрос послался в базу?
V>Или что в базе что-то изменилось?
Мне интересно два теста:
— изменилась БД правильным способом.
— Реакция кода на TimeoutException от БД
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: DDD, архитектура и т.д.
От: koodeer  
Дата: 24.11.14 17:20
Оценка:
Здравствуйте, IT, Вы писали:

IT>А почему этот вопрос ко мне, а не к тому кто предложил подобный код для обсуждения?


Потому что IT я считаю гуру, из постов которого на rsdn я много почерпнул.
Re[9]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 24.11.14 17:43
Оценка:
Здравствуйте, konsoletyper, Вы писали:

.>>Согласен. Одно мне не нравится с этими аннотациями, что они обычно навешиваются на private поле, а значит без рефлексии нормально не заинжектить, да и по публичному интерфейсу класса невозможно понять какие зависимости у него есть. Более того, если ты добавишь новое private поле для зависимости, то можно нарваться на NPE в рантайм, вместо compile time. Я предпочитаю constructor injection.


K>За навешивание аннотации на приватное поле и непредоставление возможности это же поле выставить как-либо ещё кроме DI, надо бить по рукам. Хотя бывают иногда места, когда такой шорткат оправдан, например, в интеграционных тестах, которые и так вовлекают конкретный контейнер/фреймворк, или в сильно системных вещах, вроде какой-нибудь кастомной версии BeanFactoryPostProcessor. А через конструктор и я предпочитаю инжектить, но иногда это невозможно. Например, фасад приложения чаще всего требует кучи различных сервисов, а я стараюсь избегать методов с количеством параметров больше 3-4. В таких случаях я делаю инжект через сеттеры.

По-моему зря. В данном случае надо делать исключение, пусть у конструктора будет куча параметров. Это будет явно выявлять классы, у которых слишком много зависимостей, а значит кандидат на рефакторинг. А ещё я люблю final поля, с сеттерами фокус не прокатит.
В тестовом коде да, как удобнее, можно и приваты делать.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[15]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 25.11.14 08:36
Оценка:
Здравствуйте, ., Вы писали:


.>>>В смысле mole это не специальный инструмент? Как можно без него замокать "static IQueryable<User> WithoutOpenOrders"?

V>>Это специальный инструмент. Так же, как и PowerMock.
.>powermock предполагается использовать исключительно для поддержки legacy code, использовать его при написании нового кода — моветон.
Так же, как и moles

V>>>>Хотя, если так рассматривать, что без DI вообще ничего не бывает, потому, что внутри любого сколь нибудь большого фреймворке DI хоть в каком-то виде есть.

.>>>Бывает жуткий SL, new или тупо статики.
V>>Ну это совсем в запущенных случаях только. Или совсем в простых.
.>Почему-то все мои оппоненты приводят в пример запущенный случай. Круто, чё.
То есть, "жуткий SL, new или тупо статики" это мои слова?

V>>>>Но мы ведь, надеюсь, рассматриваем случаи явного применения DI

.>>>А что значит "неявное применение DI"?
V>>Которое внутри фреймворка и при использовании наружу не выходит. Например, в .NET парсинг секций .config файла. Оно внутри само подставляет нужные классы для каждой секции, но снаружи это не видно — просто получаешь сразу типизированный результат.
.>Не понял. Можно подробнее? Судя по описанию DI тут никоим боком.
При использовании — никаким. Но внутри в конфиге указывается тип, который будет разбирать данную секцию.
Например,
<configuration>
    <configSections>
        <section name="appSettings" type="System.Configuration.AppSettingsSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false"/>
...
  <appSettings>
    <add key="ConfigurationService" value="http://localhost/Config.asmx" />
  </appSettings>

а при использовании
var config = ConfigurationManager.GetSection("appSettings");

и никто класс AppSettingsSection в явном виде не создаёт.

V>>>> ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);

.>>>Вот эта лажа Shim, Get — жуть.
V>>Это вкусовщина. Мне вот не нравится значок @ в атрибутах на джаве.
.>А find usages/rename refactoring/highligh symbol работают так же?
Достоверно на последней версии — на знаю.

V>>>>Единственное, что нужно дополнительно — это ткнуть мышой в сборку, которую нужно мочить и добавиль для неё .fakes файл, который генерируется автоматически и в котором ничего делать не надо.

.>>>Во-во. А с powermock ничего не нужно. Просто пишешь обычный java код.
V>>да, согласен, это экономит целых секунд пять.
.>Это вводит лишний мусор в проект. Хотя как я понимаю, дитям майкрософта лишняя верста не крюк, сказали надо, значит надо.
Этот "мусор" живёт в проекте тестов и никак не влияет на реальные сборки продукта. Поэтому — пофиг.
А детей майкрософта — спросите сами при случае, если интересно.

V>>Я про крутость static функций не писал. По крайней мере, в этом треде.

.>Возможно не вы, а кто-то другой. Но new в вашем коде это тот же static по сути.


V>>>>>>Я не вижу требований по использованию EF, не вижу требований к деплойменту. Я даже не вижу требования использовать в продукте .NET, если на то пошло.

.>>>>>Требования "использовать EF/.net"? Это кто такие требования выдвигает? MS?
V>>Вы проглядели предлог "не"
.>Я не понял зачем вам эти требования. Это обычно не то, что даёт БА.
Во-первых, при чём тут БА? Он не единственный определяет требования к продукту.
Во-вторых, требования к деплойменту (например, количество tier-ов) очень часто следуют из требований БА

.>>>>>Не важно, пусть вы архитектор. Принимайте сами эти решения, это я буду БА — требования с т.з. бизнеса я уже писал, поищите в треде.

V>>>>Ну так я выдвину требование: сделать всё максимально просто.
.>>>Согласен. Делайте максимально просто, но выполнив все бизнес-требования которые были в моём коде! Их напомнить? Или сами поищете в топике?
V>>Я не очень понимаю, чего Вы хотите добиться. Я код полноценного приложения в форуме писать всё равно не буду, тем более на джаве, которую не знаю.
.>Не надо код полноценного приложения. Ни один программист в реальном проекте не пишет код полноценного приложения. Он реализует конкретные бизнестребования. Вот это я и прошу. Представьте себя на месте программиста, которому поручили реализовать операцию деактивации пользователя. Бизнес-требования таковы:
.>1. Публичный web-method с параметром userId.
.>2. Вернуть определённую ошибку если userId не найден в базе
.>3. Вернуть определённую ошибку если userId соответствует текущему юзеру в сессии и если равен и флаг !mad.
.>4. Выполнить запрос к securityService, передав userId (этот сервис пишут другие, например, он делает RPC к 3rd party сервису). Воля ваша как определить интерфейс взаимодействия c 3rd party.
.>5. Записать аудит (вызов 3rd party serivce, передать userId и идентификатор операции аудита "DEACTIVATE_USER"). Аудит не писать если юзер уже был деактивирован.
.>6. Послать сообщение юзеру (вызов 3rd party serivce). Не посылать сообщение если юзер уже был деактивирован.
.>7. Собственно сделать запись в БД, что аккаунт деактивирован.
.>Вы согласны, что примерно такие требования получает типичный программист от типичного БА? (По крайней мере у нас было так)
.>У меня это заняло около минут 10-15.
Не согласен, у нас это не так. БА у нас не в курсе, что такое "Публичный web-method" или "RPC", он не технический человек. Но это не так важно, не хочу вдаваться в дискуссию что есть бизнес требования.
Если вы уже написали этот код а 10-15 минут, то зачем это делать мне ещё раз? Вы же знаете, как его написать.

V>>Всё, что я утверждал — это то, что для реализации mocks и stubs специальные фреймворки не являются необходимыми.

.>В приведённом ранее вашем коде — являются.
Потому что код был приведён для иллюстрации мока статиков, которые не были изначально предназначены для тестирования.
Если они для тестирования предназначены — фреймворки не нужны.

V>>Но если вы не знаете, как реализовать ваши бизнес требования — перечислите их, попробую помочь.

.>Я их реализовал в своём коде. Вы сказали, что я их плохо реализовал. Но не показали как же будет выглядеть хорошая реализация.
Я этого не говорил.

V>>>>И напишу одну строчку

V>>>>
V>>>>public SetUserInactive()
V>>>>{
V>>>>    using(var con = new SqlDbConnection("строка коннекта"))
V>>>>    {
V>>>>        con.Open();
V>>>>        new SqlDbbCommand("UPDATE User SET IsActive = 0 WHERE UserId = 10").ExecuteNoQuery();
V>>>>    }
V>>>>}
V>>>>

.>>>Давайте теперь тест показывайте, но только как вы утверждаете чтобы "не нужно никаких специальных инструментов, для реализации mocks и stubs для юнит тестов."
V>>Тест на что? Что запрос послался в базу?
V>>Или что в базе что-то изменилось?
.>Мне интересно два теста:
.>- изменилась БД правильным способом.
Это не юнит тест. Но если он нужен — вызвать SetUserInactive, а потом вызвать другой, который проверит состояние базы.

.>- Реакция кода на TimeoutException от БД

Замокать con.Open или ExecuteNoQuery и кинуть оттуда то, что надо. Это если код к тестам не приспосабливать.
Если приспосабливать — вынести ExecuteNoQuery в отдельный виртуальный метод, переопределить его в тесте и бросить оттуда то, что нужно.
Во втором подходе фреймворки не нужны.
Re[16]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 10:33
Оценка:
Здравствуйте, vmpire, Вы писали:

.>>>>В смысле mole это не специальный инструмент? Как можно без него замокать "static IQueryable<User> WithoutOpenOrders"?

V>>>Это специальный инструмент. Так же, как и PowerMock.
.>>powermock предполагается использовать исключительно для поддержки legacy code, использовать его при написании нового кода — моветон.
V>Так же, как и moles
Однако, в качестве примера вы приводите код, который без moles не тестируется.

V>>>Ну это совсем в запущенных случаях только. Или совсем в простых.

.>>Почему-то все мои оппоненты приводят в пример запущенный случай. Круто, чё.
V>То есть, "жуткий SL, new или тупо статики" это мои слова?
Нет, это ваш код.

V>>>>>Но мы ведь, надеюсь, рассматриваем случаи явного применения DI

.>>>>А что значит "неявное применение DI"?
V>>>Которое внутри фреймворка и при использовании наружу не выходит. Например, в .NET парсинг секций .config файла. Оно внутри само подставляет нужные классы для каждой секции, но снаружи это не видно — просто получаешь сразу типизированный результат.
.>>Не понял. Можно подробнее? Судя по описанию DI тут никоим боком.
V>При использовании — никаким. Но внутри в конфиге указывается тип, который будет разбирать данную секцию.
Понятно. DI тут никоим боком.

V>Например,

V>
V><configuration>
V>    <configSections>
V>        <section name="appSettings" type="System.Configuration.AppSettingsSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false"/>
V>...
V>  <appSettings>
V>    <add key="ConfigurationService" value="http://localhost/Config.asmx" />
V>  </appSettings>
V>

Ещё один жуткий xml.

V>а при использовании

V>
V>var config = ConfigurationManager.GetSection("appSettings");
V>

Я уж думал что я Шарп не знаю, но гугл развеял мои сомнения. Следует писать:

V>
V>AppSettingsSection config = (AppSettingsSection)ConfigurationManager.GetSection("appSettings");
V>

И надеятся, что не будет class cast exception.

V>и никто класс AppSettingsSection в явном виде не создаёт.

Самый обыкновенный махровый SL образца 90-х. Причём тут DI?

V>>>>> ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);

.>>>>Вот эта лажа Shim, Get — жуть.
V>>>Это вкусовщина. Мне вот не нравится значок @ в атрибутах на джаве.
.>>А find usages/rename refactoring/highligh symbol работают так же?
V>Достоверно на последней версии — на знаю.
Т.е. ещё требуется и специальная поддержка IDE. Жуть, да и только.

V>>>Я про крутость static функций не писал. По крайней мере, в этом треде.

.>>Возможно не вы, а кто-то другой. Но new в вашем коде это тот же static по сути.
V>
Угу. Но, по-моему это больше грустно, чем смешно.

V>>>>>>>Я не вижу требований по использованию EF, не вижу требований к деплойменту. Я даже не вижу требования использовать в продукте .NET, если на то пошло.

.>>>>>>Требования "использовать EF/.net"? Это кто такие требования выдвигает? MS?
V>>>Вы проглядели предлог "не"
.>>Я не понял зачем вам эти требования. Это обычно не то, что даёт БА.
V>Во-первых, при чём тут БА? Он не единственный определяет требования к продукту.
V>Во-вторых, требования к деплойменту (например, количество tier-ов) очень часто следуют из требований БА
Допустите, что вы вольны выбирать это на своё усмотрение, делайте как проще|удобнее. Ибо к рассматриваемому вопросу это отношения не имеет, можете даже не упоминать в этой дискусии.

.>>1. Публичный web-method с параметром userId.

.>>2. Вернуть определённую ошибку если userId не найден в базе
.>>3. Вернуть определённую ошибку если userId соответствует текущему юзеру в сессии и если равен и флаг !mad.
.>>4. Выполнить запрос к securityService, передав userId (этот сервис пишут другие, например, он делает RPC к 3rd party сервису). Воля ваша как определить интерфейс взаимодействия c 3rd party.
.>>5. Записать аудит (вызов 3rd party serivce, передать userId и идентификатор операции аудита "DEACTIVATE_USER"). Аудит не писать если юзер уже был деактивирован.
.>>6. Послать сообщение юзеру (вызов 3rd party serivce). Не посылать сообщение если юзер уже был деактивирован.
.>>7. Собственно сделать запись в БД, что аккаунт деактивирован.
.>>Вы согласны, что примерно такие требования получает типичный программист от типичного БА? (По крайней мере у нас было так)
.>>У меня это заняло около минут 10-15.
V>Не согласен, у нас это не так. БА у нас не в курсе, что такое "Публичный web-method" или "RPC", он не технический человек. Но это не так важно, не хочу вдаваться в дискуссию что есть бизнес требования.
Да не важно, допустите, что БА в курсе, что такое web-приложение и что там есть две части — клиент и сервер. Он тебе, как server-side dev выдаёт требования.

V>Если вы уже написали этот код а 10-15 минут, то зачем это делать мне ещё раз? Вы же знаете, как его написать.

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

V>>>Всё, что я утверждал — это то, что для реализации mocks и stubs специальные фреймворки не являются необходимыми.

.>>В приведённом ранее вашем коде — являются.
V>Потому что код был приведён для иллюстрации мока статиков, которые не были изначально предназначены для тестирования.
V>Если они для тестирования предназначены — фреймворки не нужны.
Меня удивляет, что многие тут приводят образцы кода, изначально не предназначенные для тестирования. Сразу пишут legacy code.

V>>>Но если вы не знаете, как реализовать ваши бизнес требования — перечислите их, попробую помочь.

.>>Я их реализовал в своём коде. Вы сказали, что я их плохо реализовал. Но не показали как же будет выглядеть хорошая реализация.
V>Я этого не говорил.

V>Это не юнит тест. Но если он нужен — вызвать SetUserInactive, а потом вызвать другой, который проверит состояние базы.

Ок. А как тестовую базу подсунуть на время теста?

.>>- Реакция кода на TimeoutException от БД

V>Замокать con.Open или ExecuteNoQuery и кинуть оттуда то, что надо. Это если код к тестам не приспосабливать.
V>Если приспосабливать — вынести ExecuteNoQuery в отдельный виртуальный метод, переопределить его в тесте и бросить оттуда то, что нужно.
V>Во втором подходе фреймворки не нужны.
Не надо слов. Код показывайте. Я уже показывал, он был короче, тем ваша эта реплика.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[24]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 12:45
Оценка:
Здравствуйте, gandjustas, Вы писали:

.>>>>Альтернативы-то какие? Писать один методы по 500 строк и забивать на тесты?

G>>>Уже выше написал: чисты функции, детерминированные функции.
.>>Покажи класс.
G>Зачем? Ты все равно не поверишь.
Коду я верю, т.к. его можно компилятором проверить, "бла-бла-бла" — не верю, особенно когда никаких доказательств нет.

.>>>>Что значит без необходимости?

G>>>То и значит. Ты сам привел код в несколько раз сложнее, чем необходимо для решения задачи, оправдав это какимто неведомым функционалом.
.>>Я показал и простой код в одну строчку (проще твоего, кстати). А вот ты сложный код не показывал. А простому коду и архитектура не нужна. Нечего и обсуждать в этом форуме.
G>Правильно, поэтому упрощай код любыми доступными средствами.
"Всё следует упрощать до тех пор, пока это возможно, но не более того."!

G>>>И про атрибуты объяснил. Но ты до сих пор не веришь, что можно написать просто.

.>>Не надо мне объяснений. Код покажи, сотый раз прошу. Пока нет кода — не поверю.
G>Я показал уже, ты не веришь. Зачем еще что-то писать?
Ты не показал ничего, что не было в моём коде, всё то же самое. Зато не показал многого из того, что было в моём коде.

.>>У меня только первый слой содержит голый параметр, в другие слои передаётся User, через него и параметры можно протаскивать.

G>Тогда зачем тебе слои? Если они у тебя только pass-through? Чтобы вызывать аудиты всякие? Тогда уж лучше атрибуты и АОП.
Атрибуты это те же слои, только выражены другими языковыми конструкциями. Со своими достоинствами и недостатками.

G>>>Во всех методах классов. Каждый метод класса зависит не только от параметров но и от состояния объекта.

.>>Верно, но с учётом того, что все мои классы stateless, то получится "Каждый метод класса зависит только от параметров".
G>Это вранье, классы получают ссылки на другие классы, которые в общем случае не stateless. Ну или ты не узнаешь об этом пока все не прочитаешь. Состояние транзитивно. Достаточно в одном классе поиметь состояние, кк все зависящие от него также получают это состояние.
Т.к. зависимостями ты управляешь сам, ты сам контролируешь и состояние. Классы сами по себе stateless.

G>>>Так ты сам приводил код. У тебя там как минимум три уровня косвенности. И это на примитивной задаче.

.>>Приведи _свой_ код.
G>А я тебе привел, один метод, этого более чем дотаточно. Нужно будет аудит — накручивается через атрибуты.
G>http://rionscode.wordpress.com/2013/03/03/implementing-audit-trails-using-asp-net-mvc-actionfilters/
G>Вот пример как аудит атрибутами накрутить.
Ок. Допустим ты это добавил (кстати код явно не проще моего).
Давай теперь проверим твою любимую поддатливость к изменениям. Появилось требование в аудит записать userId. Потом ещё одно: записать аудит только если активный юзер деактивируется.

.>>>>Код в студию, или не было.

G>>>Ты сам приводил код
.>>Я прошу привести твой правильный код, чтобы было с чем сравнивать.
G>Я тебе привел правильный код. Чем он неправильный?
Он не делает всё, что требуется. Список БА-требований тут: http://rsdn.ru/forum/design/5867830?tree=tree
Автор: .
Дата: 24.11.14


.>>>>>>Я стал спорить с тем, что фреймворк обязателен. Практически обязательным является DI паттерн, ибо он хорошо вписывается куда угодно. В моём примере никаких фреймворков нет.

G>>>>>Ты написал пример с кучей слоев. Я тебе показал, что фреймворк закрывает потребности и куча слоев не нужна. Ты начал спорить.
.>>>>Я показал что твой однострочный код с фреймворком эквивалентен однострочному коду без фреймворка, но сложнее тестировать. Код эквивалентный по фичам моему ты не показывал.
G>>>Тебе еще раз объяснить про атрибуты? Мне лениво писать более трех строк в форуме, особенно если код не решает какую-то проблему.
.>>Не надо мне объяснять. Просто покажи код. Как так лениво? Я не понимаю, ты говорил, что все эти требования примитивны, выражаются одой строчкой и парой атрибутов. Откуда вдруг более трёх взялось?
G>Потому что сама реализация атрибутов гораздо больше, чем три строки.
Мы же вроде к простоте стремимся, а тут ВНЕЗАПНО выясняется, что к трём строкам добавляется ещё "гораздо больше". Но мы это не считаем, потому что не хотим.

G>Ты же не привел реализацию аудита и проверок. Я с тем же успехом просто добавил атрибут на метод. Я тебе уже приводил этот код:

Реализацию аудита я приводил
Автор: .
Дата: 17.11.14
— 3 строки. Проверки, пусть будет 3rd party RPC.

G>
G>    [Action("Deactivate")]
G>    [HttpPost]
G>    [Audit] //Добавил
G>    [SomeChecks] //Добавил
G>    async ActionResult DeactivatePost(int userId)
G>    {
G>        var user = await ctx.Users
G>                      .WithoutOrders()
G>                      .FirstAsync(u => u.Id == userId);
G>        user.Actve = false;
G>        await ctx.SaveChagesAsync();
G>        return Redirect("Index");
G>    }
G>

G>Вот тебе код, который логически эквивалентен твоему.
G>Еще раз привожу ссылку на аудит с помощью атрибутов — http://rionscode.wordpress.com/2013/03/03/implementing-audit-trails-using-asp-net-mvc-actionfilters/ чтобы ты понял.
Не эквивалетнен. Где проверки, где идемпотентность (деактивация деактивированного)? Где посылка сообщения? Обаботака ошибочных сиутаций? В общем пройдись по списку требований и покажи как что реализовано в твоём коде.

G>>>>>С чего это? Чем явный вызов лучше атрибута?

.>>>>У метода могут быть аргументы, тут же вычисленные, можно возвращаемое значение, обработку исключений и т.п.
G>>>Аргументы откуда берутся? Видимо из запроса, больше не от куда. А в атрибуте есть контекст запроса, из которого те же параметры можно вытащить.
.>>Вычисляются кодом внутри метода. Потенциально — не только из запроса, но и из БД, настроек конфигов, сторонних сервисов и бог весть чего.
G>А что мешает тоже самое сделать в атрибутах?
Тем, что это сложнее и становится оправданным только для весьма ограниченного числа применений.

.>>>>И тестировать проще.

G>>>Вранье. Спишем просто на незнание.
.>>Атрибуты обычно только integration tests можно протестить, простой код — можно тестить с unit tests.
G>А что ты юнит-тестами проверить можешь? Только то, что вызывается аудит при выполнении метода? Так с атрибутами это без тестов проверить можно. А то, что удит правильно пишется, ты только интеграционным тестированием и покроешь.
G>Вообще полезность юнит тестов при такой примитивной логике нулевая, а по факту даже отрицательная, ибо их еще поддерживать надо.
То что вызывается в нужный момент (скажем, аудит вызывается до|после SomeChecks), что вызывается с нужными аргументами, что правильно реагирует на исключения, етс.

.>>>>Атрибут лишь хорош, если тебя в большинстве методов надо делать ровно одно и то же.

G>>>Так и есть. Мы ведь говорили об аудите, это разве не надо делать в большинстве методов?
.>>Не путаем аудит с логгированием. Нет, не надо во всех, а только в тех методах, которые делают business sensitive operations.
G>То есть в 90%, какая разница?
Нет, даже в нашем deactivateUser он либо вызывается, либо не вызывается. 50% получается.

G>>>А я не предлагаю везде, я предлагаю заменить кучи вызовов одинакового функционала на атрибуты. Например проверки, аудиты итп. А после вытаскивания такого cross-cutting функционала становятся не нужны слои, достаточно пары функций в контроллере.

.>>Код в студию.
G>См выше.
Просто слои выражаются атрибутами, сами по себе они никуда не деваются.

G>>>А тебе надо тестировать наличие атрибута? Совсем двинулся? Посмотреть нельзя?

.>>Как протестить что аудит случается только если активный юзер деактивируется? Т.е. деактивация деактивированного юзера не создаёт запись аудита.
G>Просто не показывать UI пользователю с возможностью деактивировать уже деактивированного юзера. Ты не на том уровне пытаешься проблему решать.
G>Ты понимаешь, что такая логика, как у тебя, в реальном приложении приведет к тонне говнокода?
Причём тут UI?! Что такое idempotent operation в контексте web приложения знаешь? Да и который UI? Их может быть куча разных, написанных/переписанных разыми командами|организациями.

G>>>>>В том, что меньше связей.

.>>>>Код в студию, или не было.
G>>>Код чего? Ты сам class coupling не можешь померить?
.>>Ты говоришь "меньше". Меньше чем что? Померить в твоём воображаемом коде? Нет, извини, не могу.
G>Я выше код привел — измеряй.
Там реализованно меньше половины требований, бОльшая и самая интересная часть скрыта за атрибутами. Давай всё показывай, не стесняйся.

.>>>>Код в студию, или не было.

G>>>Оо код чего? Ты же сам привел пример, который очень тяжело поддается изменениям, типа добавить колонку. Зачем тебе еще примеры?
.>>Мне нужен пример эквивалентного по фичам кода, который поддаётся изменениям легче.
G>См выше, полностью эквивалентный по фичам — вызывается два метода, которые неизвестно что делают.
Ну добавь теперь туда колонку, которая что-то в аудит пишет. В скольких местах придётся изменять? Мне кажется, что не в меньшем количестве мест, чем в моём коде.

G>Забить на юнит-тесты.

Жуть.

G>>>Яркий пример. Ты пишешь код, который ориентируется что компонент отдает даты в локали пользователя. Пишешь тест, основанный на таком поведении. А по факту он отдает в UTC, получаешь багу.

.>>А откуда я это взял, что "отдает даты в локали пользователя"? Если я это прочитал в доке, никуда не денусь, на багу придётся напороться во время интеграционного теста, поправить юнит тест, сделав его красным, затем исправить код, сделав тест зелёным. Если я это придумал сам, то сам и виноват. Но сценарий тот же.
G>У людей есть предел внимательности, такую багу пропустить очень легко. А может и не быть документации по таким мелочам вовсе.
G>Кстати зачем тебе юнит-тест, который багу не ловит, если у тебя есть интеграционный тест, который ловит?
Юнит-тест выполняется на несколько порядков быстрее интеграционного.
Юнит-тесты могут выполнять на порядок больше проверок, всяких corner case, необычных use case, т.е. самих тестов может быть на порядки больше.
Юнит-тест нужен для девелопмента. Интеграционный — для QA.

.>>>>Набор юнит-тестов показывает какая именно логика реализована и какая будет реакция.

G>>>Если ты не повторяешь в точности поведение зависимых компонентов, то вообще ничего не показывает.
.>>Показывает _ожидаемое_ поведение. Скажем, типичный HttpRequest может быть иметь 100500 различных состояний, там и куча параметров, атрибутов, куки, сессии, разные версии протокола. Это не означает, что тесты невозможны. Просто твой код будет реагировать на некое вполне определённое подмножество состояний, которые легко описывать в виде моков.
G>Ага,а по факту окажется, что в программе совершенно другой набор состояний и тест твой по сути не проверяет ничего.
Делов-то, тест гвоздями не прибит. Поправлю тест, чтобы он имел идентичный фактическому набор состояний, тут же увижу как мой код стал на это дело реагировать.

.>>unit-тест будет не сложнее тестируемого кода.

G>Это зависит от кода, если логика примитивна (поменять поле в базе), то юнит-тест окажется сложнее кода. Просто потому что setup будет занимать примерно половину объема теста.
Если писать код в стиле твоих любимых статиков "WithoutOrders", то да, может и окажется. Но тут всё просто — не надо писать так.

.>>>>Куда потребуется, туда и добавлю, может лучше в securityService подойдёт, я не понимаю точно твоё требование.

G>>>Да все ты понимаешь, и понимаешь, что такой простой кейс начинает проникать в кучу классов, увеличивая сложность поддержки.
.>>Увеличивая по сравнении с чем?
G>По сравнению с АОП
Вроде у нас была простая задача, но ВНЕЗАПНО выяснилось, что оказывается даже в простые задачи нужно пихать АОП, и мы жить не можем без "реализация атрибутов гораздо больше, чем три строки". Круто, чё.
Собственно мой код _при необходимости_ элементарно рефакторится на атрибуты. Достаточно удалить зависимость auditService из класса и на все методы, которые её использовали (укажет IDE/компилятор) навесить соответствующий атрибут. Ну и написать элементарный обработчик атрибута, который будет дёргать тот же самый (уже отлаженный, протестированный) auditService. YAGNI во всей его красе.

.>>>>А ты что предлагаешь? Навешать на все методы магический атрибут?

G>>>Да, по науке это называется AOP. Когда такой cross-cutting concern не пишется явно в коде, а инжектится извне, уменьшая связность и площадь изменений.
.>>AOP это тоже архитектурное решение, ничем не луче и ничем не хуже чем DAO/BL/etc. Просто инструмент, который надо использовать по делу.
G>Что значит "ничем не лучше"? Для конкретной задачи сильно лучше — снижает class coupling и количество строк. А BL\DAO — наоборот.
Почему у меня возникает диссонанс когда я вижу "снижает количество строк" и "гораздо больше, чем три строки"? Такое ощущение, что это просто враньё.
А class coupling снижается точно так же не только введением атрибута, но и введением интерфейса. Разницы никакой абсолютно. И ещё... Сюрприз! Но аннотации (то что в шарпе назывется атрибутами) в языке Java определяются с помощью ключевого слова "interface".

.>>Честно говоря, в начальном вопросе мне не удаётся усмотреть необходимость применения AOP.

G>А его там и нет, это ты придумал сложности с BL\DAO и AOP стал оправданным.
В том виде, в котором ты его пытаешься оправдать он как-то плохо вписывается.

.>>>>И что делать?

G>>>Побить на маленькие.
.>>Пример в студию. Особенно интересно как навешивать атрибуты на чистые статические функции.
G>Это ты придумал фукнцию с десятком аргументов. Да и вообще все сложности, которые тут обсуждаем. Так что ты приводи пример, а потом будем думать как его решать.
Я привёл список бизнес-требований, привёл код. Твоя очередь.

.>>>>Хороший дев перерисовывает это один-к-одному в виде BL.

G>>>Тогда откуда берутся 100500 сервисов, как ты в примере написал? Или ты не хороший дев?
.>>UserService и есть BL. Но помимо BL есть и другие слои.
G>Я боюсь представить что изобразил BA, что "один-к-одному в виде BL" получился userservice.
"нужна операция деактивации пользователя со следующими требованиями:..." и т.д. по тому списку.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[18]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 13:02
Оценка:
Здравствуйте, gandjustas, Вы писали:

.>>Меня удивляет, что многие тут приводят образцы кода, изначально не предназначенные для тестирования. Сразу пишут legacy code.

G>Типичная подмена понятий.
G>Тестировать можно любой детерминированный код — который выдает одинаковый результат для одного и того же набора параметров и окружения. Это верно как для ручного, так и для автоматического тестирования.
Вопрос как создать один и тот же набор — и не сдохнуть? Самое простое — с помощью юнит-тестов.

G>Ты говоришь о юнит-тестировании, которое само по себе ценность не несет, ценность в отсутствии ошибок в следствие тестирования.

G>Но эффективность юнит-тестирования очень низка — http://gandjustas.blogspot.ru/2013/07/code-review-vs-testing.html
Типичная ложная дилемма. Как будто нас заставляют выбрать юнит-тесты либо ревью. Используй и то, и то.

G>, ниже интеграционных и системных тестов. Более того есть гораздо более эффективные методы устранения ошибок, типа формальных review.

"Очень низка" и "30%"vs"40%" это как минимум излишнее преувеличение.
И ещё мне кажется, что та указанная эффективность ревью указана при наличии всего остального.

G>Фактически означает, что писать больше и более запутанного кода для того, чтобы его моно было покрыть юнит-тестами — бесполезное занятие.

Ещё более бесполезное занятие проводить формальный review без вменяемых юнит-тестов.

G>Лучше написать три строки в которых очевидно нет ошибки, чем 15 и покрыть их тестами.

Опять ложная дилемма. Ещё лучше — написать три строки и покрыть тестами.

ЗЫЖ Кстати, надо ещё учесть, что ревью и прочие human-involved методы по стоимости гораздо выше автоматических тестов.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 26.11.2014 13:20 · . Предыдущая версия .
Re[25]: DDD, архитектура и т.д.
От: Yoriсk  
Дата: 26.11.14 13:46
Оценка:
Здравствуйте, ., Вы писали:

.>Не эквивалетнен. Где проверки, где идемпотентность (деактивация деактивированного)? Где посылка сообщения?


В самом деле, ну как же мы без полезнейших сообщений типа:

Юзер уже деактивирован. Активируйте юзера и попробуйте снова


Re[26]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 13:53
Оценка:
Здравствуйте, Yoriсk, Вы писали:

.>>Не эквивалетнен. Где проверки, где идемпотентность (деактивация деактивированного)? Где посылка сообщения?

Y>В самом деле, ну как же мы без полезнейших сообщений типа:
Непонятные слова типа "идемпотентность" твоим мозгом просто игнорируются?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[27]: DDD, архитектура и т.д.
От: Yoriсk  
Дата: 26.11.14 14:08
Оценка:
Здравствуйте, ., Вы писали:

.>Непонятные слова типа "идемпотентность" твоим мозгом просто игнорируются?


Хм, судя по вашему сообщению оно непонятно скорее вам, чем мне. Если вам и вправду нужна провернка на идемпотентность для кода
user.Actve = false


то это лучше как-то внешними тестами, тело метода-то тут при чём?
Re[17]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 26.11.14 14:13
Оценка:
Здравствуйте, ., Вы писали:

.>>>powermock предполагается использовать исключительно для поддержки legacy code, использовать его при написании нового кода — моветон.

V>>Так же, как и moles
.>Однако, в качестве примера вы приводите код, который без moles не тестируется.
Потому, что это не пример кода, предназначенного для тестирования. Это пример максимально простой реализации.

V>>То есть, "жуткий SL, new или тупо статики" это мои слова?

.>Нет, это ваш код.
См. выше — это не был примеро кода, предназначенного для тестирования.

V>>Например,

V>>
...
V>>

.>Ещё один жуткий xml.
С фобиями — это к психологу, не ко мне.

.>Я уж думал что я Шарп не знаю, но гугл развеял мои сомнения. Следует писать:

V>>
V>>AppSettingsSection config = (AppSettingsSection)ConfigurationManager.GetSection("appSettings");
V>>

.>И надеятся, что не будет class cast exception.
Не противоречит тому, что я писал. Пример был не на получение конкретных значений из секции, а на получение самой секции.

V>>и никто класс AppSettingsSection в явном виде не создаёт.

.>Самый обыкновенный махровый SL образца 90-х. Причём тут DI?
Не DI, а IoC. Я их опять перепутал.

V>>>>>> ShimDateTime.NowGet = () => new DateTime(2000, 1, 1);

.>>>>>Вот эта лажа Shim, Get — жуть.
V>>>>Это вкусовщина. Мне вот не нравится значок @ в атрибутах на джаве.
.>>>А find usages/rename refactoring/highligh symbol работают так же?
V>>Достоверно на последней версии — на знаю.
.>Т.е. ещё требуется и специальная поддержка IDE. Жуть, да и только.
А что, в джаве "find usages/rename refactoring/highligh symbol" работают без IDE?

V>>Если вы уже написали этот код а 10-15 минут, то зачем это делать мне ещё раз? Вы же знаете, как его написать.

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

V>>Если они для тестирования предназначены — фреймворки не нужны.

.>Меня удивляет, что многие тут приводят образцы кода, изначально не предназначенные для тестирования. Сразу пишут legacy code.
А меня не удивляет. Не на любой код нужны юнит-тесты. Поэтому по умолчанию под них и не затачиваются.
Production quality код писать сильно дольше, чем набросать несколько строчек для иллюстрации идеи.
Если это, конечно, не идея способа юнит-тестирования.

.>- изменилась БД правильным способом.

V>>Это не юнит тест. Но если он нужен — вызвать SetUserInactive, а потом вызвать другой, который проверит состояние базы.
.>Ок. А как тестовую базу подсунуть на время теста?
Указать в конфиге проекта с юнит-тестами (который ужасный XML). Саму базу, как вариант, можно создать пустую специально для тестов.
Иногда используют копию рабочей базы — но это сложнее.

.>>>- Реакция кода на TimeoutException от БД

V>>Замокать con.Open или ExecuteNoQuery и кинуть оттуда то, что надо. Это если код к тестам не приспосабливать.
V>>Если приспосабливать — вынести ExecuteNoQuery в отдельный виртуальный метод, переопределить его в тесте и бросить оттуда то, что нужно.
V>>Во втором подходе фреймворки не нужны.
.>Не надо слов. Код показывайте. Я уже показывал, он был короче, тем ваша эта реплика.
Так всё то же самое (для варианта "замокать"):
[TestMethod]
[ExpectedException(typeof(TimeoutException))]
public void TestMethod2()
{
    using (ShimsContext.Create())
    {
        ShimSqlConnection.AllInstances.Open = c => { throw new TimeoutException("message"); };

        using (SqlConnection con = new SqlConnection("Data Source=.;Integrated Security=SSPI"))
        {
            con.Open();
            SqlCommand cmd = new SqlCommand("UPDATE User SET IsActive = 0 WHERE UserId = 10", con);
            cmd.ExecuteNonQuery();
        }
    }
}
Re[19]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.11.14 14:42
Оценка:
Здравствуйте, ., Вы писали:

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


.>>>Меня удивляет, что многие тут приводят образцы кода, изначально не предназначенные для тестирования. Сразу пишут legacy code.

G>>Типичная подмена понятий.
G>>Тестировать можно любой детерминированный код — который выдает одинаковый результат для одного и того же набора параметров и окружения. Это верно как для ручного, так и для автоматического тестирования.
.>Вопрос как создать один и тот же набор — и не сдохнуть? Самое простое — с помощью юнит-тестов.
Самое простое — взять реально окружение или его копию. И только в случае невозможности реальные компоненты заменять на "заглушки". Заглушки на то и заглушки, что никогда не повторят поведение реального компонента,поэтому тестирование кода, обложенного заглушками может проходить идеально, а на продакшене будет ломаться все.

G>>Ты говоришь о юнит-тестировании, которое само по себе ценность не несет, ценность в отсутствии ошибок в следствие тестирования.

G>>Но эффективность юнит-тестирования очень низка — http://gandjustas.blogspot.ru/2013/07/code-review-vs-testing.html
.>Типичная ложная дилемма. Как будто нас заставляют выбрать юнит-тесты либо ревью. Используй и то, и то.
У тебя время не бесконечно. Затраты на юнит тесты примерно эквивалентны затратам на написание кода, а ревью делается в 4-10 раз быстрее написания. Учитывая процент ошибок, отлавливаемых обоими методами, можно вообще отказаться от юнит-тестирования в пользу формального review и получить большую эффективность.

G>>, ниже интеграционных и системных тестов. Более того есть гораздо более эффективные методы устранения ошибок, типа формальных review.

.>"Очень низка" и "30%"vs"40%" это как минимум излишнее преувеличение.
.>И ещё мне кажется, что та указанная эффективность ревью указана при наличии всего остального.
Ты еще эффективность подели на затраты, чтобы получить "удельную эффективность", у юнит тестов она предельно низкая.
Один интеграционный тест и простой код делает ненужными десятки юнит-тестов и ловит больше ошибок.

G>>Фактически означает, что писать больше и более запутанного кода для того, чтобы его моно было покрыть юнит-тестами — бесполезное занятие.

.>Ещё более бесполезное занятие проводить формальный review без вменяемых юнит-тестов.
С чего ты взял? Для ревью вообще тесты не нужны. Юнит-тесты даже вредны получаются во многих случаях, ибо излишне запутывают код.

G>>Лучше написать три строки в которых очевидно нет ошибки, чем 15 и покрыть их тестами.

.>Опять ложная дилемма. Ещё лучше — написать три строки и покрыть тестами.
Конечно лучше, и это будет интграционный тест, а не юнит-тест.

.>ЗЫЖ Кстати, надо ещё учесть, что ревью и прочие human-involved методы по стоимости гораздо выше автоматических тестов.

А тесты сами пишутся чтоли? Их также пишут люди, которые еще и ошибаются, поэтому в идеале и тесты тоже нужно как-то проверять.
Re[26]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 17:16
Оценка:
Здравствуйте, gandjustas, Вы писали:

.>>Коду я верю, т.к. его можно компилятором проверить, "бла-бла-бла" — не верю, особенно когда никаких доказательств нет.

G>Ты и сам привел не компилируемый код для начала
Я его писал в браузере, возможно есть опечатки, но в целом код законченный. Классы|методы которые не реализованы считай что они реализованы не тобой, а поставляются другой командой и у тебя нет исходников, только интерфейс.

.>>>>Не надо мне объяснений. Код покажи, сотый раз прошу. Пока нет кода — не поверю.

G>>>Я показал уже, ты не веришь. Зачем еще что-то писать?
.>>Ты не показал ничего, что не было в моём коде, всё то же самое. Зато не показал многого из того, что было в моём коде.
G>А в твоем коде ничего "больше" не было. Пара вызовов методов реализация которых неизвестна.
Что поделаешь, она и вправду неизвестна! Они поставляются 3rd party. Например, те самые securityChecks в моём предыдущем проекте был просто вызов через REST системы fraud monitoring. auditService был вызов через SOAP системы написанной на C под HP-UX. Я реально видел практически ровно столько кода, сколько я написал тут. Как ты предлагаешь писать интеграционные тесты?
И да, наш БА знает все эти компонеты системы и как они взаимодействуют.

.>>Атрибуты это те же слои, только выражены другими языковыми конструкциями. Со своими достоинствами и недостатками.

G>Не те же. Атрибуты ортогональны.
В чём принципиальная разница-то? Интерфейсы такие же.

G>>>Это вранье, классы получают ссылки на другие классы, которые в общем случае не stateless. Ну или ты не узнаешь об этом пока все не прочитаешь. Состояние транзитивно. Достаточно в одном классе поиметь состояние, кк все зависящие от него также получают это состояние.

.>>Т.к. зависимостями ты управляешь сам, ты сам контролируешь и состояние. Классы сами по себе stateless.
G>Ты забыл про внешние зависимости. Внезапно большинство ORM отдают stateful классы контекста, которые у тебя прямо или косвенно заинжектены во все сервисы.
Не понял к чему это. new MyDB у тебя разве не было? Обещал чистые функции, а вдруг эти чистые функции через статики ВНЕЗАПНО лезут куда попало. Хуже того, просто навешенный атрибут тоже ВНЕЗАПНО может добавить состояние (тот же аудит).

.>>Ок. Допустим ты это добавил (кстати код явно не проще моего).

.>>Давай теперь проверим твою любимую поддатливость к изменениям. Появилось требование в аудит записать userId. Потом ещё одно: записать аудит только если активный юзер деактивируется.
G>Тоже через атрибуты\AOP решается, но навешивается на контекст, а не на контроллер.
Код давай.

.>>Он не делает всё, что требуется. Список БА-требований тут: http://rsdn.ru/forum/design/5867830?tree=tree
Автор: .
Дата: 24.11.14

G>Послать нафиг БА и решать реальную проблему, а не надуманную.
Угу, очень конструктивно.

.>>Мы же вроде к простоте стремимся, а тут ВНЕЗАПНО выясняется, что к трём строкам добавляется ещё "гораздо больше". Но мы это не считаем, потому что не хотим.

G>Ты и сам этот код не привел
Так нет его.

.>>Не эквивалетнен. Где проверки, где идемпотентность (деактивация деактивированного)?

G>Сам догадаешься куда if добавить?
Нет. Покажи.

.>>Где посылка сообщения?

G>Атрибут добавишь или помочь? Или добавь одну строку в метод, в принципе без разницы.
Помоги.

.>>Обаботака ошибочных сиутаций?

G>В атрибуте
Покажи.

G>Не догадался еще как хорошая архитектура выглядит?

Вместо трёх методов надо сделать шесть аттрибутов и столько же обработчиков атрибутов. Правильно догадался? Ты уверен, что это хорошая архитектура?

.>>Тем, что это сложнее и становится оправданным только для весьма ограниченного числа применений.

G>На практике не сложнее и подходит гораздо чаще, чем ты думаешь. Просто в javaне принято делать атрибуты, а в .NET это используется постоянно.
Принято, но для упрощения кода, а не для усложнения.

.>>То что вызывается в нужный момент (скажем, аудит вызывается до|после SomeChecks), что вызывается с нужными аргументами, что правильно реагирует на исключения, етс.

G>Сделай интеграционный тест. Ты можешь из кода вызвать не просто весь пайплайн asp.net MVC, а можно даже сделать веб-вызов, который отработает все блоки.
Как ты, например, в интеграционном тесте проверишь последовательность вызова? Например, аудит должен записаться перед someCheck.

G>http://blog.nwoolls.com/2013/05/30/integration-testing-asp-net-mvc-projects-with-specsfor-mvc/

G>Один такой тест сделает ненужным около 20 юнит-тестов. Причем 20 юнит тестов будут отлавливать меньше ошибок, чем один такой тест.
И работать он будет в >100 раз медленнее. Сейчас у нас в проекте 16500 юнит-тестов, работают 3 минуты; 1500 интеграционных, работают 30 минут; и 8000 системных, работают 2 часа на кластере из ~30 машин.
Даже с твоим пессимизмом, если создать 40 юнит-тестов они будут отлавливать больше ошибок, чем один интеграционный, отнимая в 10 раз меньше времени.

.>>>>Не путаем аудит с логгированием. Нет, не надо во всех, а только в тех методах, которые делают business sensitive operations.

G>>>То есть в 90%, какая разница?
.>>Нет, даже в нашем deactivateUser он либо вызывается, либо не вызывается. 50% получается.
G>Так ты определись это аудит или лог. Аудит для действия пользователя, лог для поведения приложения. Соотвественно аудит пишется всегда, его надо в атрибуты, а лог может зависеть от условий — его методами. Но для лога есть отличное решение в .NET — ETW называется.
Аудит действий, конечно. Если какой-то вызов был сделан дважды из-за проблем с сетью, это не означает что юзер совершил действие дважды.

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

G>Нет, слои и AOP — разные вещи. Слои есть когда у тебя есть отношение порядка, когда ты моешь сказать что один слой находится выше\ниже другого. Для АОП такого отношение нет.
Точнее оно выражается неявно. Ты не можешь разные атрибуты вешать куда попало. Только в определённом порядке, только в определённые места.

G>>>Просто не показывать UI пользователю с возможностью деактивировать уже деактивированного юзера. Ты не на том уровне пытаешься проблему решать.

G>>>Ты понимаешь, что такая логика, как у тебя, в реальном приложении приведет к тонне говнокода?
.>>Причём тут UI?! Что такое idempotent operation в контексте web приложения знаешь? Да и который UI? Их может быть куча разных, написанных/переписанных разыми командами|организациями.
G>А при чем тут идемпотентность? Присваивание значения полю — самая идемпотентная операция, даже никакой логики не нужно.
Так не бывает требования "присвоить значение полю", есть требование "деактивировать юзера" — с посылкой сообщений, и прочим.

G>>>См выше, полностью эквивалентный по фичам — вызывается два метода, которые неизвестно что делают.

.>>Ну добавь теперь туда колонку, которая что-то в аудит пишет. В скольких местах придётся изменять? Мне кажется, что не в меньшем количестве мест, чем в моём коде.
G>Тебе кажется.
Развей мои сомнения.

G>>>Забить на юнит-тесты.

.>>Жуть.
G>В случае примитивной логики юнит-тесты только мешают. Лучше интеграционные тесты делать.
В случае примитивной логики вообще никакие тесты не нужны.

G>>>Кстати зачем тебе юнит-тест, который багу не ловит, если у тебя есть интеграционный тест, который ловит?

.>>Юнит-тест выполняется на несколько порядков быстрее интеграционного.
G>И что? Если он не ловит баги, то без разницы как быстро он выполняется.
Если тест не ловит баги, его нужно переписать так, чтобы он ловил баги.

.>>Юнит-тесты могут выполнять на порядок больше проверок, всяких corner case, необычных use case, т.е. самих тестов может быть на порядки больше.

G>Это хорошо? Как раз наоборот, программисты начинают гоняться за покрытием неважных\несуществующих сценариев, вместо важных.
G>Например та же деактивация пользователя. Ты будешь все сценарии покрывать тестами, особенно много у тебя получится для всяких ошибок — польователь не найден, пользователь уже деактивирован итп.
G>А по факту на UI выводятся только существующие пользователи и кнопка "деактивировать" есть только у активных пользователей. То есть в нормальной работе ни один из протестированных тобой сценариев не будет вызываться.
Так это и есть типичное говноприложение, которое только работает при нормальной работе. А как что, таймаут какой, дисконнект, юзер что-то в неожиданном порядке нажал и... бууум!

.>>Юнит-тест нужен для девелопмента. Интеграционный — для QA.

G>Тебе лично чем юнит тесты помогают? Ловят ошибки, которые ты не совершил бы, если бы не пытался покрыть код юнит-тестами?
Значительно ускоряют разработку. Я имплеменчу хитрое условие, не очень понятно как оно сработает при таких-то данных — мне не нужно ничего поднимать, запускать, деплоить, я просто стартую юнит-тест, занимает секунду.
Позволяет легко репродьюсить баги. Увидел в логе хрень какую-то, возникает предположение от чего оно может быть — быстренько создаю тестик и проверяю предполжение, а не дико кликаю 100500 кнопок на UI в надежде угадать последовательность.
Упрощают эксперименты. "А что если я тут вместо Array воткну Set, должно заработать быстрее, но а что сломается-то?".
Ну и регрессия конечно.

G>>>Ага,а по факту окажется, что в программе совершенно другой набор состояний и тест твой по сути не проверяет ничего.

.>>Делов-то, тест гвоздями не прибит. Поправлю тест, чтобы он имел идентичный фактическому набор состояний, тут же увижу как мой код стал на это дело реагировать.
G>А как ты узнаешь фактический набор состояний?
Доку почитаю, логи полистаю, людей поспрашиваю, небольшой тестик накидаю, подебажу, етс.

G>А зачем вообще писать тест, когда ты не знаешь фактический набор состояний?

А как я вообще смогу написать хоть какой-то код, если я не знаю с чем он должен работать? Если я не знаю факты, я не смогу писать сам код, а если знаю, то могу это выразить в виде тестовых данных, а значит и написать тест.

G>>>Это зависит от кода, если логика примитивна (поменять поле в базе), то юнит-тест окажется сложнее кода. Просто потому что setup будет занимать примерно половину объема теста.

.>>Если писать код в стиле твоих любимых статиков "WithoutOrders", то да, может и окажется. Но тут всё просто — не надо писать так.
G>Как раз WithoutOrders прекрасно тестируется, ибо чистая функция.А как ты предлагаешь писать?
G>У меня есть прекрасный пример, когда люди думали что "не надо писать так". Нафигаличи репозиториев с кучей методов под каждый запрос. Репозитории мокали в тестах и в тестах все прекрасно работало. А вот сами репозитории содержали тучи ошибок в запросах, ибо методы репозиториев размножались копипастой, где-то была просто кривая обработка параметров, поэтому в одних случаях работало прекрасно, в других выдавало неверные данные, а в третьих получался неверных запрос.
G>В итоге порототип с двумя контроллерами, 10 экшенами и 500 юнит-тестами при выкатывании на staging грохнулась с таким шумом, что чуть не уволили тимлида.
Юнит-тесты не отменяют необходимость интеграционных и системных.

.>>>>Увеличивая по сравнении с чем?

G>>>По сравнению с АОП
.>>Вроде у нас была простая задача, но ВНЕЗАПНО выяснилось, что оказывается даже в простые задачи нужно пихать АОП, и мы жить не можем без "реализация атрибутов гораздо больше, чем три строки". Круто, чё.
G>Так ты сам простую задачу усложнил до состояния, что стал нужен АОП. Это твое достижение, не нужно делиться лаврами.
G>И самое хреновое, что такое и в реальных проектах встречается чуть менее, чем всегда.
Ещё раз, я добавил бизнес-требований, чтобы задача стала больше похоже на реальную. Лично мне никогда не встечались задачи "проапдейтить поле в базе".

.>>А class coupling снижается точно так же не только введением атрибута, но и введением интерфейса. Разницы никакой абсолютно. И ещё... Сюрприз! Но аннотации (то что в шарпе назывется атрибутами) в языке Java определяются с помощью ключевого слова "interface".

G>А ты считал? Я вот считал. Class coupling это грубо говоря количество ссылок между классами. Атрибуты вообще убирают ссылки, а интерфейсы — нет. Так что разница есть и весьма заметная.
Между _классами_, а не интерфейсами. Единственная разница — если у тебя зависимость от интерфейса — ты её обязан удовлетворить (возможно подставив пустышку), в случае атрибута — его можно игнорировать. Что просто делает зависимость неявной.

.>>>>Честно говоря, в начальном вопросе мне не удаётся усмотреть необходимость применения AOP.

G>>>А его там и нет, это ты придумал сложности с BL\DAO и AOP стал оправданным.
.>>В том виде, в котором ты его пытаешься оправдать он как-то плохо вписывается.
G>Это лишь твое мнение, которые ты не можешь обосновать никакими объективными факторами.
Объективный факт — в твоём attribute-driven подходе будет больше кода.

G>>>>>Побить на маленькие.

.>>>>Пример в студию. Особенно интересно как навешивать атрибуты на чистые статические функции.
G>>>Это ты придумал фукнцию с десятком аргументов. Да и вообще все сложности, которые тут обсуждаем. Так что ты приводи пример, а потом будем думать как его решать.
.>>Я привёл список бизнес-требований, привёл код. Твоя очередь.
G>У меня никогда не получится функции с 10+ аргументами при любых бизнес-требованиях. У тебя получается — тогда приводи пример, будем разбираться.
У меня тоже. Но у тебя и не получилось обещанных чистых функций.

.>>>>UserService и есть BL. Но помимо BL есть и другие слои.

G>>>Я боюсь представить что изобразил BA, что "один-к-одному в виде BL" получился userservice.
.>>"нужна операция деактивации пользователя со следующими требованиями:..." и т.д. по тому списку.
G>И? ты самое важное в итоге пропустил
G>Но даже при этом:
G>1) Что такое операция, почему она у тебя превратилась в метод веб-сервиса?
То что дёргается UI-клиентами (коих десяток и половина из них зааутсорсена).

G>2) Откуда у тебя взялись слои, о них в требованиях написано?

Из декомпозиции требований на простые части.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[18]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 17:59
Оценка:
Здравствуйте, vmpire, Вы писали:

.>>>>powermock предполагается использовать исключительно для поддержки legacy code, использовать его при написании нового кода — моветон.

V>>>Так же, как и moles
.>>Однако, в качестве примера вы приводите код, который без moles не тестируется.
V>Потому, что это не пример кода, предназначенного для тестирования. Это пример максимально простой реализации.
Что за лажа. Максимально простой реализацией будет "psql -c 'UPDATE User SET IsActive = 0 WHERE UserId = 10'".
Почему у Вас простота противоречит с тестируемостью? Можно же писать очень простой и при этом тестируемый код.

.>>И надеятся, что не будет class cast exception.

V>Не противоречит тому, что я писал. Пример был не на получение конкретных значений из секции, а на получение самой секции.
Не понимаю к чему это тогда. Вроде Вы доказывали, что фреймворки не обязательны, всё круто, а тут опять, жить не можете без очередного фреймворка IoC-контейнера.

V>>>и никто класс AppSettingsSection в явном виде не создаёт.

.>>Самый обыкновенный махровый SL образца 90-х. Причём тут DI?
V>Не DI, а IoC. Я их опять перепутал.
Не надо их путать. Разница принципиальна. И вообще тогда это всё бессмысленно в этом обсуждении.

V>>>>>Это вкусовщина. Мне вот не нравится значок @ в атрибутах на джаве.

.>>>>А find usages/rename refactoring/highligh symbol работают так же?
V>>>Достоверно на последней версии — на знаю.
.>>Т.е. ещё требуется и специальная поддержка IDE. Жуть, да и только.
V>А что, в джаве "find usages/rename refactoring/highligh symbol" работают без IDE?
Они работают для Джавы, а не для очередного поделия гордо названного framework. Т.е. Powermock никакой специальной поддержки IDE не требует. Грубо говоря, закидываешь "powermock.jar" в зависимости и всё, используй на здоровье.

V>>>Если вы уже написали этот код а 10-15 минут, то зачем это делать мне ещё раз? Вы же знаете, как его написать.

.>>Я знаю, но тут многие меня убеждают, что так писать нельзя, но никто не показывает как же можно.
V>Ну так другие пусть и пишут примеры. Я лично считаю, что как писать и что использовать — нужно выбирать под конкретный проект.
V>Не зная всех подробностей советовать что-то применять — бессмысленно. Можно советовать, разве что, рассмотреть на применимость.
Перечисленные мной требования не ограничивают выбор. Да и вообще они на выбор не влияют.

V>>>Если они для тестирования предназначены — фреймворки не нужны.

.>>Меня удивляет, что многие тут приводят образцы кода, изначально не предназначенные для тестирования. Сразу пишут legacy code.
V>А меня не удивляет. Не на любой код нужны юнит-тесты. Поэтому по умолчанию под них и не затачиваются.
V>Production quality код писать сильно дольше, чем набросать несколько строчек для иллюстрации идеи.
V>Если это, конечно, не идея способа юнит-тестирования.
Вроде мы тут в Архитектуре, и, как я понимаю, должны обсуждать как же именно писать production quality код. По крайней мере, мой код production quality (за исключением описания конекста, он обычно берёт некоторые настройки откуда-нибудь, а не захардкоженые значения). Для обмена идей как проапдейтить поле в базе есть форумы СУБД и C#.

V>Указать в конфиге проекта с юнит-тестами (который ужасный XML). Саму базу, как вариант, можно создать пустую специально для тестов.

V>Иногда используют копию рабочей базы — но это сложнее.
Ладно, согласен. Просто да, пример неудачный, тестируется SQL, который только интеграционными тестами можно покрыть.

.>>Не надо слов. Код показывайте. Я уже показывал, он был короче, тем ваша эта реплика.

V>Так всё то же самое (для варианта "замокать"):
А, понятно, опять этот кошмарик. Но я не понимаю, почему Вы в форуме Архитектура приводите пример кода, который без фреймворков не тестируется и вообще не работает? Он же по Вашим словам является legacy.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[20]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 18:16
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>>Типичная подмена понятий.

G>>>Тестировать можно любой детерминированный код — который выдает одинаковый результат для одного и того же набора параметров и окружения. Это верно как для ручного, так и для автоматического тестирования.
.>>Вопрос как создать один и тот же набор — и не сдохнуть? Самое простое — с помощью юнит-тестов.
G>Самое простое — взять реально окружение или его копию. И только в случае невозможности реальные компоненты заменять на "заглушки". Заглушки на то и заглушки, что никогда не повторят поведение реального компонента,поэтому тестирование кода, обложенного заглушками может проходить идеально, а на продакшене будет ломаться все.
Есть же staging. Но пока дойдёт до большого, медленного и страшного staging, есть ещё куча тестов. feedback ошибок происходит на порядки быстрее.

G>>>Ты говоришь о юнит-тестировании, которое само по себе ценность не несет, ценность в отсутствии ошибок в следствие тестирования.

G>>>Но эффективность юнит-тестирования очень низка — http://gandjustas.blogspot.ru/2013/07/code-review-vs-testing.html
.>>Типичная ложная дилемма. Как будто нас заставляют выбрать юнит-тесты либо ревью. Используй и то, и то.
G>У тебя время не бесконечно. Затраты на юнит тесты примерно эквивалентны затратам на написание кода, а ревью делается в 4-10 раз быстрее написания. Учитывая процент ошибок, отлавливаемых обоими методами, можно вообще отказаться от юнит-тестирования в пользу формального review и получить большую эффективность.
Зато код при использовании юнит-тестов пишется гораздо быстрее. Тебе не нужно всё компилить, собирать, деплоить чтобы протестировать как оно реально работает. Сделал изменение через секунду увидел на что и как оно повлияло.
Ещё один интересный эффект, запуск всего имеет место замедляться с ростом приложения, тогда как размер юнитов обычно ограничен, притом бывает даже на уровне code style checks, скажем, "класссы не больше 1000 строк".

.>>"Очень низка" и "30%"vs"40%" это как минимум излишнее преувеличение.

.>>И ещё мне кажется, что та указанная эффективность ревью указана при наличии всего остального.
G>Ты еще эффективность подели на затраты, чтобы получить "удельную эффективность", у юнит тестов она предельно низкая.
G>Один интеграционный тест и простой код делает ненужными десятки юнит-тестов и ловит больше ошибок.
Он ловит ошибки интеграции, а не имплементации. Т.е. когда ожидания одного участка кода не согласуются с работой друго участка.

G>>>Фактически означает, что писать больше и более запутанного кода для того, чтобы его моно было покрыть юнит-тестами — бесполезное занятие.

.>>Ещё более бесполезное занятие проводить формальный review без вменяемых юнит-тестов.
G>С чего ты взял? Для ревью вообще тесты не нужны. Юнит-тесты даже вредны получаются во многих случаях, ибо излишне запутывают код.
Они помогают быстрее понять суть изменений. Скажем, добавил новый юнит-тест для обработки corner case и переписал код, чтобы этот corner case обрабатывался правильно. По тесту будет легко увидеть что конкретно поменялось с т.з. логики, а изменения самого кода могут быть большими и страшными, особенно если порефакторил чуток.

G>>>Лучше написать три строки в которых очевидно нет ошибки, чем 15 и покрыть их тестами.

.>>Опять ложная дилемма. Ещё лучше — написать три строки и покрыть тестами.
G>Конечно лучше, и это будет интграционный тест, а не юнит-тест.
Чем лучше? Можно подольше кофе попить пока оно всё запускается?

.>>ЗЫЖ Кстати, надо ещё учесть, что ревью и прочие human-involved методы по стоимости гораздо выше автоматических тестов.

G>А тесты сами пишутся чтоли? Их также пишут люди, которые еще и ошибаются, поэтому в идеале и тесты тоже нужно как-то проверять.
Логика тестов обычно тривиальна, там нет никаких if/for/etc, просто последовательное линейное выполнение нескольких выражений. Зачем их проверять?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[19]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 26.11.14 20:27
Оценка:
Здравствуйте, ., Вы писали:

.>>>Однако, в качестве примера вы приводите код, который без moles не тестируется.

V>>Потому, что это не пример кода, предназначенного для тестирования. Это пример максимально простой реализации.
.>Что за лажа. Максимально простой реализацией будет "psql -c 'UPDATE User SET IsActive = 0 WHERE UserId = 10'".
да, можно и так.

.>Почему у Вас простота противоречит с тестируемостью? Можно же писать очень простой и при этом тестируемый код.

Дело не в противоречии. Дело в том, что это пример другого.
Если есть пример формы с синими кнопками и другой пример с квадратными кнопками, это не значит, что квадратные кнопки противоречат синим.

.>>>И надеятся, что не будет class cast exception.

V>>Не противоречит тому, что я писал. Пример был не на получение конкретных значений из секции, а на получение самой секции.
.>Не понимаю к чему это тогда. Вроде Вы доказывали, что фреймворки не обязательны, всё круто, а тут опять, жить не можете без очередного фреймворка IoC-контейнера.
Вы предложили, чтобы я сам определил требования к коду и написал код на основе своих требований.
В мои требования тестируемость не входила.
А к чему вы это предложили — я не знаю.

V>>>>>>Это вкусовщина. Мне вот не нравится значок @ в атрибутах на джаве.

.>>>>>А find usages/rename refactoring/highligh symbol работают так же?
V>>>>Достоверно на последней версии — на знаю.
.>>>Т.е. ещё требуется и специальная поддержка IDE. Жуть, да и только.
V>>А что, в джаве "find usages/rename refactoring/highligh symbol" работают без IDE?
.>Они работают для Джавы, а не для очередного поделия гордо названного framework. Т.е. Powermock никакой специальной поддержки IDE не требует. Грубо говоря, закидываешь "powermock.jar" в зависимости и всё, используй на здоровье.
А что, "find usages/rename refactoring/highligh symbol" делает Powermock? При чём тут он вообще, если вопрос был про "find usages/rename refactoring/highligh symbol"

V>>Production quality код писать сильно дольше, чем набросать несколько строчек для иллюстрации идеи.

V>>Если это, конечно, не идея способа юнит-тестирования.
.>Вроде мы тут в Архитектуре, и, как я понимаю, должны обсуждать как же именно писать production quality код. По крайней мере, мой код production quality (за исключением описания конекста, он обычно берёт некоторые настройки откуда-нибудь, а не захардкоженые значения). Для обмена идей как проапдейтить поле в базе есть форумы СУБД и C#.
Писать код и обсуждать, как писать код это несколько разные действия.


.>>>Не надо слов. Код показывайте. Я уже показывал, он был короче, тем ваша эта реплика.

V>>Так всё то же самое (для варианта "замокать"):
.>А, понятно, опять этот кошмарик. Но я не понимаю, почему Вы в форуме Архитектура приводите пример кода, который без фреймворков не тестируется и вообще не работает? Он же по Вашим словам является legacy.
Во-первых, потому, что, как вы сами заметили, для кода есть отдельные форумы.
Во вторых, можно и без моков (но Вам же опять не понравится):
    class Database
    {
        private SqlConnection con = new SqlConnection("Data Source=.;Integrated Security=SSPI"));
        public virtual void OpenConnection() { con.Open() } 
        public virtual void ExecuteNoQuery(string sql) 
        { 
            SqlCommand cmd = new SqlCommand(sql, con);
            cmd.ExecuteNonQuery();
        } 
    }

    class MyService
    {
        private Database db;
        public MyService(Database db) { this.db = db; }

        public void InactivateUser()
        {
            db.OpenConnection();
            db.ExecuteNoQuery("UPDATE User SET IsActive = 0 WHERE UserId = 10")
        }

    }

    class Test
    {
        private class TestDatabase : Database
        {
            public override void OpenConnection()
            {
                  throw new TimeoutException("message");
            }
        }

        [TestMethod] 
        [ExpectedException(typeof(TimeoutException))] 
        public void TestMethod3() 
        {
            var s = new MyService(new TestDatabase());
            s.InactivateUser();
        }
    }
Re[20]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 21:03
Оценка:
Здравствуйте, vmpire, Вы писали:

.>>>>Однако, в качестве примера вы приводите код, который без moles не тестируется.

V>>>Потому, что это не пример кода, предназначенного для тестирования. Это пример максимально простой реализации.
.>>Что за лажа. Максимально простой реализацией будет "psql -c 'UPDATE User SET IsActive = 0 WHERE UserId = 10'".
V>да, можно и так.
И Вы правда считаете такой код будет адекватным ответом на оригинальный вопрос топикстартера о том как строить архитектуру приложения?

.>>Почему у Вас простота противоречит с тестируемостью? Можно же писать очень простой и при этом тестируемый код.

V>Дело не в противоречии. Дело в том, что это пример другого.
V>Если есть пример формы с синими кнопками и другой пример с квадратными кнопками, это не значит, что квадратные кнопки противоречат синим.
Непонятная аналогия. Скорее надо сравнивать как езду на мотоцикле без шлема или со шлемом.

.>>Не понимаю к чему это тогда. Вроде Вы доказывали, что фреймворки не обязательны, всё круто, а тут опять, жить не можете без очередного фреймворка IoC-контейнера.

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

V>В мои требования тестируемость не входила.

V>А к чему вы это предложили — я не знаю.
Да, возможно это моё мировосприятие. Мне кажется, что нетривиальное качественное приложение невозможно написать без тестирования. Поэтому у меня вошло в привычку писать тестируемый код, как и ездить на мотоцикле в шлеме. Ведь тестируемый код по сложности идентичен нетестируемому.

V>>>А что, в джаве "find usages/rename refactoring/highligh symbol" работают без IDE?

.>>Они работают для Джавы, а не для очередного поделия гордо названного framework. Т.е. Powermock никакой специальной поддержки IDE не требует. Грубо говоря, закидываешь "powermock.jar" в зависимости и всё, используй на здоровье.
V>А что, "find usages/rename refactoring/highligh symbol" делает Powermock? При чём тут он вообще, если вопрос был про "find usages/rename refactoring/highligh symbol"
Ладно. Объясню с начала. Допустим у нас есть legacy code "class A{static int f(){return 33;}}". Мы хотим написать тесткейс, который мочит этот самый f(). В powermock это будет выглядеть как:
when(A.f()).thenReturn(42)

в moles это будет выглядеть как
ShimA.fGet = () => 42

Внимание вопрос. Если я в IDE сделаю find usages/rename для A.f(), будет ли оно работать сразу везде или нужна специальная поддержка IDE данного мок-фреймворка? Ответ для powermock: нет, не нужна. Ответ для moles: да, нужна.
Понятно?

V>>>Production quality код писать сильно дольше, чем набросать несколько строчек для иллюстрации идеи.

V>>>Если это, конечно, не идея способа юнит-тестирования.
.>>Вроде мы тут в Архитектуре, и, как я понимаю, должны обсуждать как же именно писать production quality код. По крайней мере, мой код production quality (за исключением описания конекста, он обычно берёт некоторые настройки откуда-нибудь, а не захардкоженые значения). Для обмена идей как проапдейтить поле в базе есть форумы СУБД и C#.
V>Писать код и обсуждать, как писать код это несколько разные действия.
Правильно. Но обсуждение без примеров кода это пустая болтовня.
Какую именно идею Вы иллюстрировали тем своим примером кода? Какое отношение эта идея имеет к обсуждению архитектуры приложения?

.>>>>Не надо слов. Код показывайте. Я уже показывал, он был короче, тем ваша эта реплика.

V>>>Так всё то же самое (для варианта "замокать"):
.>>А, понятно, опять этот кошмарик. Но я не понимаю, почему Вы в форуме Архитектура приводите пример кода, который без фреймворков не тестируется и вообще не работает? Он же по Вашим словам является legacy.
V>Во-первых, потому, что, как вы сами заметили, для кода есть отдельные форумы.
V>Во вторых, можно и без моков (но Вам же опять не понравится):
Хорошо, но уже лучше. ВНЕЗАПНО появилось два слоя и код стал походить на мой.
А тут меня почему-то убеждают, что так писать нельзя.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 26.11.2014 21:14 · . Предыдущая версия .
Re[21]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 26.11.14 21:44
Оценка:
Здравствуйте, ., Вы писали:

.>>>Что за лажа. Максимально простой реализацией будет "psql -c 'UPDATE User SET IsActive = 0 WHERE UserId = 10'".

V>>да, можно и так.
.>И Вы правда считаете такой код будет адекватным ответом на оригинальный вопрос топикстартера о том как строить архитектуру приложения?
Нет. Но вопрос ставился по другому: самому определить требования и самому реализовать.

.>>>Почему у Вас простота противоречит с тестируемостью? Можно же писать очень простой и при этом тестируемый код.

V>>Дело не в противоречии. Дело в том, что это пример другого.
V>>Если есть пример формы с синими кнопками и другой пример с квадратными кнопками, это не значит, что квадратные кнопки противоречат синим.
.>Непонятная аналогия. Скорее надо сравнивать как езду на мотоцикле без шлема или со шлемом.
Объясняю подробно: пример простого кода не обязан быть тестируемым. Пример тестируемого кода не обязан быть простым.
Но это не значит, что простота противоречит тестируемости.

.>Да, возможно это моё мировосприятие. Мне кажется, что нетривиальное качественное приложение невозможно написать без тестирования. Поэтому у меня вошло в привычку писать тестируемый код, как и ездить на мотоцикле в шлеме. Ведь тестируемый код по сложности идентичен нетестируемому.

Это сильно зависит от задачи. Бывает так что юнит тестирование вообще мало полезно или не применимо.

.>Ладно. Объясню с начала. Допустим у нас есть legacy code "class A{static int f(){return 33;}}". Мы хотим написать тесткейс, который мочит этот самый f(). В powermock это будет выглядеть как:

.>
when(A.f()).thenReturn(42)

.>в moles это будет выглядеть как
.>
ShimA.fGet = () => 42

.>Внимание вопрос. Если я в IDE сделаю find usages/rename для A.f(), будет ли оно работать сразу везде или нужна специальная поддержка IDE данного мок-фреймворка? Ответ для powermock: нет. Ответ для moles: да.
.>Понятно?
Теперь понятно.
Нет, для moles метод автоматически не переименуется, хотя Shims и перегенирируеся. Я согласен, что это недостаток.
Но недостаток этот происходит от того, что PowerMock и Moles реализуют разные концепции: PowerMock переписывает код на лету в рантайме при загрузке класса (см. "хаки байткода и прочий шлак"). а Moles — не переписывает код, а генерирует для него обёртки для тестов во время компиляции. Если код нужно переписывать в рантайме — есть другие средства, например TypeMock. Правда и тестироваться при этом будет переписанный код, а не оригинальный.
Для корректного сравнения продуктов берите хотя бы продукты одного назначения.


V>>>>Production quality код писать сильно дольше, чем набросать несколько строчек для иллюстрации идеи.

V>>>>Если это, конечно, не идея способа юнит-тестирования.
.>>>Вроде мы тут в Архитектуре, и, как я понимаю, должны обсуждать как же именно писать production quality код. По крайней мере, мой код production quality (за исключением описания конекста, он обычно берёт некоторые настройки откуда-нибудь, а не захардкоженые значения). Для обмена идей как проапдейтить поле в базе есть форумы СУБД и C#.
V>>Писать код и обсуждать, как писать код это несколько разные действия.
.>Правильно. Но обсуждение без примеров кода это пустая болтовня.
.>Какую именно идею Вы иллюстрировали тем своим примером кода? Какое отношение эта идея имеет к обсуждению архитектуры приложения?
Каким "тем?" Я вообще код приводить не хотел, это Вы настаивали.
А в данном случае я вообще писал про то, почему большинство предпочитает писать в качестве примера простейший код.

.>>>>>Не надо слов. Код показывайте. Я уже показывал, он был короче, тем ваша эта реплика.

V>>>>Так всё то же самое (для варианта "замокать"):
.>>>А, понятно, опять этот кошмарик. Но я не понимаю, почему Вы в форуме Архитектура приводите пример кода, который без фреймворков не тестируется и вообще не работает? Он же по Вашим словам является legacy.
V>>Во-первых, потому, что, как вы сами заметили, для кода есть отдельные форумы.
V>>Во вторых, можно и без моков (но Вам же опять не понравится):
.>Хорошо, но уже лучше. ВНЕЗАПНО появилось два слоя и код стал походить на мой.
А их и так два по определению задачи. Вопрос только в том, появятся они во время написания кода (послелний пример с виртуальными функциями) компиляции (Moles) или во время выполнения (PowerMock).

.>А тут меня почему-то убеждают, что так писать нельзя.

Это вопрос к тем, кто убеждает.
Писать по всякому можно, иногда один подход более уместен, иногда другой.
Re[22]: DDD, архитектура и т.д.
От: . Великобритания  
Дата: 26.11.14 22:14
Оценка:
Здравствуйте, vmpire, Вы писали:

.>>Непонятная аналогия. Скорее надо сравнивать как езду на мотоцикле без шлема или со шлемом.

V>Объясняю подробно: пример простого кода не обязан быть тестируемым. Пример тестируемого кода не обязан быть простым.
V>Но это не значит, что простота противоречит тестируемости.
Я понимаю, что не обязан. Но я утверждаю что _может_. И мне непонятно, зачем писать нетестируемый код в качестве примера в этом форуме?

.>>Да, возможно это моё мировосприятие. Мне кажется, что нетривиальное качественное приложение невозможно написать без тестирования. Поэтому у меня вошло в привычку писать тестируемый код, как и ездить на мотоцикле в шлеме. Ведь тестируемый код по сложности идентичен нетестируемому.

V>Это сильно зависит от задачи. Бывает так что юнит тестирование вообще мало полезно или не применимо.
Да, бывает, например что-нибудь низкоуровневое, железячное. Поведение железки мочить довольно сложно. Но в этом топике эти маргинальные случаи вроде не рассматривались.

.>>Понятно?

V>Теперь понятно.
V>Нет, для moles метод автоматически не переименуется, хотя Shims и перегенирируеся. Я согласен, что это недостаток.
V>Но недостаток этот происходит от того, что PowerMock и Moles реализуют разные концепции: PowerMock переписывает код на лету в рантайме при загрузке класса (см. "хаки байткода и прочий шлак"). а Moles — не переписывает код, а генерирует для него обёртки для тестов во время компиляции. Если код нужно переписывать в рантайме — есть другие средства, например TypeMock. Правда и тестироваться при этом будет переписанный код, а не оригинальный.
V>Для корректного сравнения продуктов берите хотя бы продукты одного назначения.
Непонятно что за обёртки. Судя по моему знанию C# это неверно, гугл подтверждает: "Moles rewrites MSIL method bodies before they are compiled into assembly code by the .NET runtime". В общем-то в точности то же, что и powermock делает. Почему ему ещё требуются генерация compile time — не знаю, может ограничения .NET runtime, может просто кривая архитектура moles.

.>>Правильно. Но обсуждение без примеров кода это пустая болтовня.

.>>Какую именно идею Вы иллюстрировали тем своим примером кода? Какое отношение эта идея имеет к обсуждению архитектуры приложения?
V>Каким "тем?" Я вообще код приводить не хотел, это Вы настаивали.
V>А в данном случае я вообще писал про то, почему большинство предпочитает писать в качестве примера простейший код.
Блин. Я вообще уже перестал что Вы хотите доказать. Мы вроде тут об архитектуре, а вы приводите в качестве примера кода sql запрос. Зачем??

V>>>Во-первых, потому, что, как вы сами заметили, для кода есть отдельные форумы.

V>>>Во вторых, можно и без моков (но Вам же опять не понравится):
.>>Хорошо, но уже лучше. ВНЕЗАПНО появилось два слоя и код стал походить на мой.
V>А их и так два по определению задачи. Вопрос только в том, появятся они во время написания кода (послелний пример с виртуальными функциями) компиляции (Moles) или во время выполнения (PowerMock).
Почему "два по определению"? Люди тут говорят, что вообще никаких слоёв, и всё можно запрограммировать атрибутами.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[23]: DDD, архитектура и т.д.
От: vmpire Россия  
Дата: 27.11.14 09:41
Оценка:
Здравствуйте, ., Вы писали:

.>>>Непонятная аналогия. Скорее надо сравнивать как езду на мотоцикле без шлема или со шлемом.

V>>Объясняю подробно: пример простого кода не обязан быть тестируемым. Пример тестируемого кода не обязан быть простым.
V>>Но это не значит, что простота противоречит тестируемости.
.>Я понимаю, что не обязан.
Сейчас — видимо, да. Ранее
Автор: .
Дата: 26.11.14
— очевидно, нет.

.>Но я утверждаю что _может_.

Кто же спорит, что может

.>И мне непонятно, зачем писать нетестируемый код в качестве примера в этом форуме?

На этот вопрос я уже отвечал

.>>>Да, возможно это моё мировосприятие. Мне кажется, что нетривиальное качественное приложение невозможно написать без тестирования. Поэтому у меня вошло в привычку писать тестируемый код, как и ездить на мотоцикле в шлеме. Ведь тестируемый код по сложности идентичен нетестируемому.

V>>Это сильно зависит от задачи. Бывает так что юнит тестирование вообще мало полезно или не применимо.
.>Да, бывает, например что-нибудь низкоуровневое, железячное. Поведение железки мочить довольно сложно. Но в этом топике эти маргинальные случаи вроде не рассматривались.
Как раз железни мочить в целях тестирования софта не очень сложно. Но сложно мочить всякие ETL, бизнес-логику в базе, да мало ли ещё что...
Конечно, проще объявить то, что не нравится либо не подходит под картину мира "маргинальными случаями". Но кому-то другому это всё равно реализовывать.

V>>Для корректного сравнения продуктов берите хотя бы продукты одного назначения.

.>Непонятно что за обёртки. Судя по моему знанию C# это неверно, гугл подтверждает: "Moles rewrites MSIL method bodies before they are compiled into assembly code by the .NET runtime". В общем-то в точности то же, что и powermock делает. Почему ему ещё требуются генерация compile time — не знаю, может ограничения .NET runtime, может просто кривая архитектура moles.
Ну, так глубоко я не копал, надобности не было. Оно просто работает, да и нужно не часто.

.>>>Правильно. Но обсуждение без примеров кода это пустая болтовня.

.>>>Какую именно идею Вы иллюстрировали тем своим примером кода? Какое отношение эта идея имеет к обсуждению архитектуры приложения?
V>>Каким "тем?" Я вообще код приводить не хотел, это Вы настаивали.
V>>А в данном случае я вообще писал про то, почему большинство предпочитает писать в качестве примера простейший код.
.>Блин. Я вообще уже перестал что Вы хотите доказать. Мы вроде тут об архитектуре, а вы приводите в качестве примера кода sql запрос. Зачем??
А вы не смешивайте все вопросы в один. Читайте, на какую именно фразу ("Меня удивляет, что многие тут приводят образцы кода, изначально не предназначенные для тестирования") я отвечал.
В ответе на данную фразу никакого кода вообще не было.

V>>>>Во-первых, потому, что, как вы сами заметили, для кода есть отдельные форумы.

V>>>>Во вторых, можно и без моков (но Вам же опять не понравится):
.>>>Хорошо, но уже лучше. ВНЕЗАПНО появилось два слоя и код стал походить на мой.
V>>А их и так два по определению задачи. Вопрос только в том, появятся они во время написания кода (послелний пример с виртуальными функциями) компиляции (Moles) или во время выполнения (PowerMock).
.>Почему "два по определению"?
Два слоя: 1. тестируемый код, 2. слой интерфейса к базе либо его мок

.>Люди тут говорят, что вообще никаких слоёв, и всё можно запрограммировать атрибутами.

То, как именно программировать количество слоёв не меняет. Можно их создать явно, можно навешать атрибутов, чтобы слой создался другим кодом в рантайме.
Re[12]: DDD, архитектура и т.д.
От: __SPIRIT__ Россия  
Дата: 06.12.14 15:22
Оценка:
Здравствуйте, gandjustas, Вы писали:

.>>* конкретные ошибки (не ололо 500, всё пропало) NotFoundException, SuicideNotAllowedException,

G>Это неверный подход. Надо не позволять пользователю выполнить такие операции, скрывать\деактивировать кнопки на UI. А если же какойнить хакер сделает такой вызов, то "все пропало" достаточно.

Это как? В момент времени t0 все было хорошо, ui показал некую кнопку. В момент t1 юзер нажал на нее и ушел реквест.
Но в момент t1-dt пришел другой реквест на сервер и ситуация поменялась. База уже другая, операцию выполнить нельзя. Код должен отреагировать и отправить нормальную ошибку.
Ололо конечно нам удобно, но юзеру то что с ней делать?

.>>* вызов проверки безопасности someSecurity.checkSomething

G>Так вообще писать нельзя. Проверки ролей безопасности должны быть в фильрах (атрибутах), а проверки данных в запросе.

А если у нас одна операция и в зависимости от роли пользователя можно редактировать разный набор полей?
Роль1 не может редактировать — отсекается в самом начале
Роль2 может редактировать только имя
Роль3 три может редактировать все.

Как ты 2 и 3 будешь отличать внутри?

З.Ы.
С большей частью более чем согласен
Re[13]: DDD, архитектура и т.д.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 07.12.14 00:39
Оценка:
Здравствуйте, __SPIRIT__, Вы писали:

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


.>>>* конкретные ошибки (не ололо 500, всё пропало) NotFoundException, SuicideNotAllowedException,

G>>Это неверный подход. Надо не позволять пользователю выполнить такие операции, скрывать\деактивировать кнопки на UI. А если же какойнить хакер сделает такой вызов, то "все пропало" достаточно.

__S>Это как? В момент времени t0 все было хорошо, ui показал некую кнопку. В момент t1 юзер нажал на нее и ушел реквест.

__S>Но в момент t1-dt пришел другой реквест на сервер и ситуация поменялась. База уже другая, операцию выполнить нельзя. Код должен отреагировать и отправить нормальную ошибку.
__S>Ололо конечно нам удобно, но юзеру то что с ней делать?
Ничего, нажать назад и повторить. Потому что подобная проблема будет проявляться в 0,001% случаев.
При такой вероятности ценности в подобных проверках никаких.

Это, кстати, прекрасная иллюстрация разницы программистского и продуктового мышления. Программист считает, что любой сценарий одинаково важен. С точки зрения логики работы программы X==true и X==false имеют одинаковую ценность. С точки зрения пользователя X==false не имеет значения если имеет вероятность менее 1%, а в некоторых случаях и менее 5%. Поэтому заморачиваться в таких случаях категорически не имеют смысла.

__S>А если у нас одна операция и в зависимости от роли пользователя можно редактировать разный набор полей?

__S>Роль1 не может редактировать — отсекается в самом начале
__S>Роль2 может редактировать только имя
__S>Роль3 три может редактировать все.

__S>Как ты 2 и 3 будешь отличать внутри?


Делается атрибут на модель, аналогичный Authorize на контролер, подменяется Edit Tempate для сущностей (или ченить анологичное), чтобы учитывал атрибут.
Re[2]: DDD, архитектура и т.д.
От: Крякозавр  
Дата: 15.12.14 16:33
Оценка:
Здравствуйте, Baudolino, Вы писали:

B>[offtopic] Первый раз задавался этим вопросом, кажется, лет пять назад, но воз и ныне там. Доколе .Net-чики будут здесь обсуждать целесообразность трехзвенки? Когда уже появятся (ну или станут общепринятыми, если они уже есть) адекватные паттерны и фреймворки, которые раз и на всегда ответят на этот вопрос?


Трехслойки?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.