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[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[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[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[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[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[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[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, архитектура и т.д.
От: 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[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 может делать что-то другое (просто в файл писать, а не в бд).
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.