Re[7]: Передача контекста через множество уровней абстракции приложения
От: Sharov Россия  
Дата: 31.05.16 11:07
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Нееет. Ты похоже просто с такими системами не сталкивался. В них типовая проблема — цепочка вызовов в 5-7 слоёв, в каждом из которых могут потребоваться свои зависимости, о которых вызывающий код ничего знать не должен. И общее число зависимостей — не единицы, а ближе к паре десятков. Ибо они добавляются/убираются буквально на ходу, под текущий набор требований.


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

S>Что-то типа сильно нелинейного пайплайна получается.


Кодом людям нужно помогать!
Re[9]: Передача контекста через множество уровней абстракции приложения
От: Sharov Россия  
Дата: 31.05.16 11:10
Оценка:
Здравствуйте, Sinix, Вы писали:

S>В крайнем случае бывает вот так: завтра клиент попросит — выкинем часть расчётов в пул задач, интернет поменяем на чтение переданного файла, лог будем показывать бегущей строкой на клиенте а IDumper вообще заменим на реализацию клиента.


Тут, по-моему, за ради душевного спокойствия разработчиков лучше сделать fork.
Кодом людям нужно помогать!
Re[10]: Передача контекста через множество уровней абстракции приложения
От: Sinix  
Дата: 31.05.16 11:26
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Я как-то до этого обходится инжектированием зависимостей через конструктор.

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


0>Тут тонкий нюанс. Я в целом против такого подхода, когда в теле метода происходит извлечение ссылок из некоторого Service Provider`а.

Я уж не знаю, капсом писать что ли
У топикстартера требуется подсовывать зависимости _динамически_. Грубо говоря

using (var c = BeginTransaction())
{
  service.DoSomething(obj1);
  c.Commit();
}
using (var c2 = BeginTransaction())
{
  service.DoSomething(obj2);
  c2.Commit();
}

инстанс сервиса один, контекст (в этом примере — транзакции) — разные. Куча аллокаций терпима, когда у тебя десятки запросов в минуту. Когда больше — хошь-не-хошь на GC уже приходится посматривать.
Re[8]: Передача контекста через множество уровней абстракции приложения
От: Sinix  
Дата: 31.05.16 11:40
Оценка:
Здравствуйте, Sharov, Вы писали:


S>>Нееет. Ты похоже просто с такими системами не сталкивался. В них типовая проблема — цепочка вызовов в 5-7 слоёв, в каждом из которых могут потребоваться свои зависимости, о которых вызывающий код ничего знать не должен. И общее число зависимостей — не единицы, а ближе к паре десятков. Ибо они добавляются/убираются буквально на ходу, под текущий набор требований.


S>Тот случай, когда телега впереди лошади и злоупотребление DP и IoC.

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

Разумеется, там есть свои нюансы, как по тестированию, так и по проектированию API / соблюдению контрактов, но это уже совсем оффтоп.


S>Тут, по-моему, за ради душевного спокойствия разработчиков лучше сделать fork.

Я ж говорю — не сталкивался :P

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

В общем, всё легко и просто, но ровно до тех пор, пока исключения единичны. Когда нет — нет
Re[9]: Передача контекста через множество уровней абстракции приложения
От: Sharov Россия  
Дата: 31.05.16 11:46
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Форк поддерживать денюжкка стоит. Даже не столько денюжка, сколько время — разработчиков, QA, внедренцев, саппорта, возни с индивидуальным хостингом и т.д. и т.п. И с появлением различных комбинаций доработок это время меньше не становится


Тут надо от заказчика плясать -- если генеральный и денежный, то воленс-ноленс. В противном случае дороже будет поддерживать кодовую базу, которая всё и для всех. Для отдельного разового заказчика можно и форк.
Кодом людям нужно помогать!
Re[6]: Передача контекста через множество уровней абстракции приложения
От: Sinix  
Дата: 31.05.16 12:07
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>А есть какие-нибудь best practices по разработке бизнес-логики с использованием контекстов?

Разумеется.
-1. рассмотреть идею 0x7be — все объекты короткоживущие, получаются вызовом метода самого контекста, в момент вызова контекст заполняет в объекте ссылку на себя.
Самый надёжный способ передавать контекст, если нет проблем с излишними аллокациями.

0. По возможности заменить CallContext на AsyncLocal.

Дальше начинается магия, т.к. все прочие варианты работают очень своеобразны, требуют минимум .net 4.5 _и_ это поведение не документировано.
1. ВНИМАТЕЛЬНО прочитать вот этот пост. Особенно текст, выделенный жирным.

2. Учитывать, что CallContext текущего потока подменяется асинхронным контекстом в момент вызова delegate.EndInvoke(). То же самое — для ремотинга для некоторых каналов.

3. Для WCF сервисов c хостингом под IIS по слухам CallContext работает не всегда. Деталей не помню, гугл находит всякую фигню.

4. По тем же слухам, в какой-то из версий, callContext терялся после .ConfigureAwait(false) Я подозреваю, что это было в какой-то из бет, поскольку наши тесты на этом не падали.

5. Тесты-тесты-тесты. Если вы решили использовать недокументированный вариант, то покройте тестами основные комбинации вызовов — через новый поток / thread pool / таски / await, await поверх IO (всякие ReadXxxAsync), комбинации .BeginInvoke() / .EndInvoke(), SynchronizationContext etc. Вставьте отладочные ассерты, которые будут проверять корректность заполнения контекста.


LW>Как донести до читателей-писателей кода, что вот эта штука зависит от того же ILogger, который необходимо проинициализировать, а если этого не сделать, то рухнешь на ровном месте с NotInitializedException? А может и не рухнешь, а рухнет у кастомера, который пошёл по самому редкому сценарию, не покрытому тесами и избежавшего пристального взора QA?


Ну а это уже общая для всех DI-фреймворков проблема. Самый простой способ — используем только документированные зависимости, которые оформлены в виде extension-методов, они должны быть доступны всегда. Для экзотики или пишем тесты + отладочные ассерты, или пусть себе падает — это будет возможно лучше, чем запуск в продакшне непроверенного кода.
Re: Передача контекста через множество уровней абстракции приложения
От: · Великобритания  
Дата: 31.05.16 13:20
Оценка: 4 (2)
Здравствуйте, LWhisper, Вы писали:

LW>Проявляется это во всём, начиная с циклических референсов в сборках, благодаря рефлекшену, и заканчивая Constructor Injection c 28 зависимостями.

На одном проекте так и получилось. Вначале всякие static context юзали, потом IoC-контейнеры потом всё это выкинули и заменили тупым простым кодом с 28 зависимостями.
Единственный нюанс — код структурирован — собственно сами компоненты, где логика, юнит-тесты, у них как правило мало зависимостей (если становится много — хороший знак порезать класс). И связывающий код (wiring) — вот тут и появляются 28 параметров в конструкторе, но этот связывающий код — плоский, элементарный, тесты не нужны. Преимущество перед всякими ServiceLocator-подходами как тут упомянутыми Context — ошибки зависимостей будут проверяться компилятором, а не в runtime.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[7]: Передача контекста через множество уровней абстракции приложения
От: LWhisper  
Дата: 31.05.16 13:47
Оценка:
Здравствуйте, Sinix, Вы писали:
S>Ну а это уже общая для всех DI-фреймворков проблема. Самый простой способ — используем только документированные зависимости, которые оформлены в виде extension-методов, они должны быть доступны всегда. Для экзотики или пишем тесты + отладочные ассерты, или пусть себе падает — это будет возможно лучше, чем запуск в продакшне непроверенного кода.
Да вот особо то и не поможет, так как благодаря прозрачному протаскиванию контекста сквозь слои, где-нибудь уровне на 5ом, будет дёрнут этот самый экстеншен вида Context.GetLogger(), а компонент тремя уровнями выше кто-нибудь импортирует и дёрнет за хвост, даже не догадываясь, что чуть глубже есть какая-то привязка к контексту.
Re[8]: Передача контекста через множество уровней абстракции приложения
От: Sinix  
Дата: 31.05.16 14:33
Оценка: 6 (1)
Здравствуйте, LWhisper, Вы писали:

LW>Да вот особо то и не поможет, так как благодаря прозрачному протаскиванию контекста сквозь слои, где-нибудь уровне на 5ом, будет дёрнут этот самый экстеншен вида Context.GetLogger(), а компонент тремя уровнями выше кто-нибудь импортирует и дёрнет за хвост, даже не догадываясь, что чуть глубже есть какая-то привязка к контексту.


В смысле? Документированные зависимости должны быть доступны всегда. Их собственно для этого и документируют.

Ну и прозрачное протаскивание на практике абсолютно необязательно.
Re[6]: Передача контекста через множество уровней абстракции приложения
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 31.05.16 15:08
Оценка: 4 (1) +5
Здравствуйте, LWhisper, Вы писали:

LW>CallContext — шикарная штука!


Скверная это штука. Пользоваться стоит только при полном отсутствии альтернатив

LW>А есть какие-нибудь best practices по разработке бизнес-логики с использованием контекстов? Как донести до читателей-писателей кода, что вот эта штука зависит от того же ILogger, который необходимо проинициализировать, а если этого не сделать, то рухнешь на ровном месте с NotInitializedException?


Главная рекомендация — не вносить динамику там, где можно обойтись статикой. Особенно это любителей DI контейнеров касается.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re: Передача контекста через множество уровней абстракции приложения
От: · Великобритания  
Дата: 01.06.16 07:42
Оценка: +1 :)
Здравствуйте, LWhisper, Вы писали:

LW>Некоторое время душу терзают мысли о Dependeny Injection. Но в компании уже был неудачный опыт внедрения этой штуки (о котором(ой) я ни сном, ни духом), закончившийся выпиливанием оной с большим количеством матюгов. В общем, не прижилось, да оно и понятно — время жизни и порядок инициализации объектов были непоняты от слова совсем, и зачастую людям возвращались объекты, которые они не просили, и были зарегистрированы вообще не пойми кем, когда и с какой целью.

Тут кстати путаница чувствуется. DI это один из способов IoC. А "штука" это, думается, имеешь в виду IoC-container. А регистрация это Service Locator.
В общем, разберись в 4 терминах: DI, SL, IoC, IoC-container.
В большинстве случаев тебе нужен constructor based DI, изредка, в динамической плагинной архитектуре какие-то части возможно потребуют SL. IoC-container хорош для быстрой разработки, но в долгоживущем большом проекте лучше его выкинуть нафиг.

LW>Возьмём, для примера, логирование.

Тут, мне видится не один логгер подменяемый в "глобальной переменной" thread local контекста, а должны быть разные логгеры в разных компонентах (constructor based DI), плюс возможно несколько фабрик, для разных целей: interface UserLoggerFactory {Logger getLoggerForUser(Credentials);} , interface RemoteHostLoggerFactory {Logger getLoggerForHost(InetAddress);} и т.п.
Т.е. каждый компонент имеет данный в конструкторе логгер и менять его нельзя. Ведь ты, думаю, не хочешь, чтобы компонент аутентификации пользователя, даже если он вдруг понадобился повторно, стал писать свои "секретные" логи в папку юзера. Друими словами, все перечисленные тобой сценарии говорят о том, что каждый конкретный компонент имеет свой логгер, переданный на этапе создания компонента; конечно, получается лажа, если все компоненты пытаются лезть и и даже менять одну и ту же глобальную переменную.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: Передача контекста через множество уровней абстракции приложения
От: LWhisper  
Дата: 01.06.16 10:43
Оценка:
Здравствуйте, ·, Вы писали:

·>Тут, мне видится не один логгер подменяемый в "глобальной переменной" thread local контекста, а должны быть разные логгеры в разных компонентах (constructor based DI), плюс возможно несколько фабрик, для разных целей: interface UserLoggerFactory {Logger getLoggerForUser(Credentials);} , interface RemoteHostLoggerFactory {Logger getLoggerForHost(InetAddress);} и т.п.

·>Т.е. каждый компонент имеет данный в конструкторе логгер и менять его нельзя. Ведь ты, думаю, не хочешь, чтобы компонент аутентификации пользователя, даже если он вдруг понадобился повторно, стал писать свои "секретные" логи в папку юзера. Друими словами, все перечисленные тобой сценарии говорят о том, что каждый конкретный компонент имеет свой логгер, переданный на этапе создания компонента; конечно, получается лажа, если все компоненты пытаются лезть и и даже менять одну и ту же глобальную переменную.

Идея замечательная, но не реальная. Я даже не буду сейчас касаться GC, поплохеет разработчикам, которым придётся добавить логер в 25597 классов.
Re[3]: Передача контекста через множество уровней абстракции приложения
От: · Великобритания  
Дата: 01.06.16 11:40
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>·>Т.е. каждый компонент имеет данный в конструкторе логгер и менять его нельзя. Ведь ты, думаю, не хочешь, чтобы компонент аутентификации пользователя, даже если он вдруг понадобился повторно, стал писать свои "секретные" логи в папку юзера. Друими словами, все перечисленные тобой сценарии говорят о том, что каждый конкретный компонент имеет свой логгер, переданный на этапе создания компонента; конечно, получается лажа, если все компоненты пытаются лезть и и даже менять одну и ту же глобальную переменную.


LW>Идея замечательная, но не реальная. Я даже не буду сейчас касаться GC,

А какие проблемы с GC в .net?

LW>поплохеет разработчикам, которым придётся добавить логер в 25597 классов.

Что значит добавить? Если он в 25597 классах уже есть, то просто надо потихотньку начать заменять глобальный вызов "StaticContext.getCurrentLogger()" на constructor injection. В IDEA — два рефакторинга: extract "StaticContext.getCurrentLogger()" as field with initialisation in constructor, затем в конструкторе вынести это же выражение как параметр. Потом потихоньку перекраивать создание объектов, вынося wiring в отдельное место.

Но чуда тут быть никакого не может. Если у тебя есть 25597 мест в которых всё плохо, то придётся менять все 25597 чтобы стало всё хорошо. Technical debt он такой...
Мы помню как-то меняли как-то статический вызов Clock.getCurrentMillis() на this.clock.getCurrentMillis()... тысячи мест, и ничего, за пару дней справились.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Передача контекста через множество уровней абстракции приложения
От: LWhisper  
Дата: 02.06.16 09:59
Оценка:
Здравствуйте, ·, Вы писали:

·>Что значит добавить? Если он в 25597 классах уже есть, то просто надо потихотньку начать заменять глобальный вызов "StaticContext.getCurrentLogger()" на constructor injection. В IDEA — два рефакторинга: extract "StaticContext.getCurrentLogger()" as field with initialisation in constructor, затем в конструкторе вынести это же выражение как параметр. Потом потихоньку перекраивать создание объектов, вынося wiring в отдельное место.


·>Но чуда тут быть никакого не может. Если у тебя есть 25597 мест в которых всё плохо, то придётся менять все 25597 чтобы стало всё хорошо. Technical debt он такой...

·>Мы помню как-то меняли как-то статический вызов Clock.getCurrentMillis() на this.clock.getCurrentMillis()... тысячи мест, и ничего, за пару дней справились.
Зачем?
Re[5]: Передача контекста через множество уровней абстракции приложения
От: · Великобритания  
Дата: 02.06.16 10:20
Оценка: 27 (2)
Здравствуйте, LWhisper, Вы писали:

LW>·>Мы помню как-то меняли как-то статический вызов Clock.getCurrentMillis() на this.clock.getCurrentMillis()... тысячи мест, и ничего, за пару дней справились.

LW>Зачем?
У нас время в разных местах используется из разных источников — либо время операционки (физическое), либо "официальное" время некоего главного компонента, чем гарантируется детерминированность выполнения. Это позволяет не только создавать идентичные реплики для failover, но и точно проигрывать в тестовом окружении production logs чтобы воспроизводить проблемы.
А ещё для разных авто-тестов. Во-первых, когда нет статиков проще писать юнит-тесты. А у нас ещё есть функциональные тесты, когда система целиком запускается на множестве машин и эмулируются всякие события, например, ежедневная рассылка отчётов, поэтому нужно уметь менять время во всей системе (мы зовём эту фичу tardis ) пока работает некий сценарий, поэтому время операционки нам не подходит.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Передача контекста через множество уровней абстракции приложения
От: Tom Россия http://www.RSDN.ru
Дата: 07.06.16 20:24
Оценка:
LW>В связи с чем вопрос — что делать?
Намешено к сожалению у тебя куча вопросов. Разных.

LW>Тут и Id сессии, и Id текущей задачи, и идентификатор пользователя

Тут надо отделить мух от котлет. Контекст или State ака данные — это котлеты.
Классы аля логгер — это мухи. Это две абсолютно разные сущности.
Со стейтом можно поступать по разному, если этот стейт надо тягать с собой и хранить в памяти то по сути вариантов всего два.
Делать это явно или не явно. Не явный — передавать его как то неявно Либо через TLS (ThreadStatic) либо дважды неявно — создавать Child контейнер в котором регистрировать Singleton который будет возвращать state либо полностью либо по частям. Child хорошо тем что код который пользуется стэйтом по сути ничего не знает о его времени жизни. Он просто будет зависеть от интерфейса который позволяет работать со стейтом и всё. А минус этого подхода в том что по сути он скрывает в себе некую магию. Т.е. где то создаётся некий child контейнер, в котором есть определённый класс зарегистрированный определённым способом. И не дай бог кто то попросит этот интерфейс у родительского контейнера. Неявная передача контекста через ThreadStatic это проблемы с асинхронным кодом (async/await). Как вариант решения проблем с ThreadStatic и async/await может быть LogicalCallContext. В 4.5 он умеет "теч" между потоками.

Другой, тупой и простой вариант — тупо передавать стейт в каждом вызове каждого метода которому он может понадобится.
Из минусов лично: Лично я вижу парочку
1. Мы часто мы будем передавать "лишние данные", например, в класс необходимо передать информацию только о юзере и не нужны данные сессии и другие. Частично эту проблему можно решить разбив контекст на несколько мелких абстрактных классов или интерфейсов и один наследник. Соответсвенно кому нужен весь контекст будут принимать в параметрах наследник, кому части — части.
2. Может это звучит странно — но это точно не модный способ, он слишком простой, он НЕ даёт вам самовыразиться, показать вам знания аспектов работы контейнеров итп... Он выглядит топорно.
Из плюсов:
1. Это супер просто и абсолютно точно минимизирует различные возмодные проблемы с другими вариантами аля чайлд контейнера, ThreadStatic или LogicalCallContext.
2. Explicit всегда лучше Implicit это аксиома. Имея контекст явно его использование можно легко отслеживать по коду.
3. Это очень дёшево с точки зрения производительности.


Лично я бы сейчас выбрал явную передачу контекста. Причём контекст так же может реализовывать интерфейс который могут принимать классы аля логер в свои методах логирования. Заметь они НЕ будут принимать этот интерфейс в конструкторе а именно в методах и это нормально, ибо контекст это state, данные и они в идеале должны плавать через стек.
Народная мудрось
всем все никому ничего(с).
Re: Передача контекста через множество уровней абстракции приложения
От: itslave СССР  
Дата: 30.07.16 13:41
Оценка: +1 :)
Здравствуйте, LWhisper, Вы писали:

LW>Возьмём, для примера, логирование.


Выкинуть весь этот треш и прикрутить NLog + Sumologic(или альтернативы по вкусу). Логгер обьявлять в каждом классе приватной статической переменной.
Re[5]: Передача контекста через множество уровней абстракции приложения
От: Vladek Россия Github
Дата: 30.07.16 15:11
Оценка:
Здравствуйте, Sinix, Вы писали:

S>У топикстартера как раз одна из проблем — как этот контейнер по всей цепочке вызовов протащить. Есть какие-нибудь идеи?


Его не нужно никуда протаскивать. Достаточно воспользоваться механизмом событий — у компонента будет набор релевантных событий, а логгер будет на них подписан. Логгер на них будет подписан — совсем не значит, что код логгера надо будет трогать. Будет отдельный слушатель событий, который будет слушать нужные события и пополнять лог, используя логгер.
Re: Передача контекста через множество уровней абстракции приложения
От: Vladek Россия Github
Дата: 30.07.16 15:43
Оценка: +1
Здравствуйте, LWhisper, Вы писали:

LW>В связи с чем вопрос — что делать?

LW>Есть некий воркфлоу приложения, который, продираясь через множество уровней абстракции, уходит всё глубже в дебри internal-сборок и попутно обрастает параметрами-зависимостями, в нижней точки своего падения приходя к тем самым 28 аргументам в конструкторе очередного класса, без которых он жить не может.

Один из главных принципов ООП: лампочка вкручивает себя в лампу сама.

LW>Тут и Id сессии, и Id текущей задачи, и идентификатор пользователя, и логгер и ещё чёрт знает что, на лицо ужасное, стрёмное внутри.


Как компонент, делающий реальную работу, зависит от идентификаторов и журналирования (многие тут очень узко понимают термины, поэтому уточняю: это сакральный логгинг по-русски)? Чтобы выполнить операцию, нужна инструкция, а не журнал — логично? Операция зависит от инструкции, журнал от состояния операции. Зависимость в твоём приложении иная — журнал зависит от компонентов, а не они от него. Если это осознать, то станет легче распутывать клубок зависимостей.

Как журналу узнавать о текущем состоянии компонентов и пополнять себя?
Как компонентам сообщать о своём состоянии независимым образом?

В .NET есть механизм событий — он решает оба вопроса простым способом. Не стоит его игнорировать.

LW>Хочется выбросить это в какой-нибудь легко доступный (как в плане кода, так и в плане перфоманса) контекст, с которым будет легко общаться.

LW>Для данных таковым контекстом выступает БД, благодаря тому, что она всего одна, то доступ осуществляется через синглтон (когда-нибудь это обязательно выстрелит -_-).
LW>А вот для всего остального он отсутствует, и тянутся эти зависимости per aspera ad astra.

LW>Возьмём, для примера, логирование.

LW>Вот запускается приложение и моментально начинает писать лог. Имя лога спускается в аргументах командой строки.
LW>Окей, от этого трудно отказаться (хотя и можно), но пока терпимо.
LW>Происходит аутентификация пользователя. Теперь лог пишется уже в каталог этого горемычного юзера.
LW>Юзер выполняет некоторую команду, лог пишется всё в ту же папку, но уже в файл с именем выполняемой команды.
LW>Команда начинает параллелиться и работает с разными компьютерами в сети. Каждая задача начинает писать лог в файл с именем упомянутой выше команды + имя компьютера.

Это перечисление событий, на которые должен реагировать журнал(ы). Нужно продумать механизм публикации событий и подписки на них. Стандартные события .NET, доморощенный диспетчер событий — это уже детали.

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


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

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


Компоненты должны публиковать события, журнал их слушать.

LW>Некоторое время душу терзают мысли о Dependeny Injection. Но в компании уже был неудачный опыт внедрения этой штуки (о котором(ой) я ни сном, ни духом), закончившийся выпиливанием оной с большим количеством матюгов. В общем, не прижилось, да оно и понятно — время жизни и порядок инициализации объектов были непоняты от слова совсем, и зачастую людям возвращались объекты, которые они не просили, и были зарегистрированы вообще не пойми кем, когда и с какой целью. В общем, всё плохо, и опять же из-за непродуманной архитектуры, отрисованой на коленке, чтобы хоть как-то работало в стиле write-only.


Внедрение зависимостей — краткосрочный этап при запуске программы. Он тут просто ни при чём.
Re: Передача контекста через множество уровней абстракции приложения
От: rm822 Россия  
Дата: 30.07.16 22:46
Оценка: +1
LW>Тут и Id сессии, и Id текущей задачи, и идентификатор пользователя, и логгер и ещё чёрт знает что, на лицо ужасное, стрёмное внутри.
Ну грубо говоря у тебя проблема с тем что в конструкторы ты передаешь кусок стейта, нарубленого в салат ?
myCtor(int userId, int sessionId, int taskid)

в тупую развернуть зависимости, если задача хочет непременно записать в лог свой ид, ок, пускай мучается
class MyTask
{
  Session session;
  void WriteLog(string);
};
class Session
{
  User user;
  void WriteLog(string);
}

myCtor(MyTask )



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

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

class MyLowLevelContraption
{
  MyLowLevelContraption(int userId, int sessionId, int taskid, IMyLogger); // вот такого быть не должно
  MyLowLevelContraption(MyTask ); //и такого тоже, хотя оно уже лучше
  MyLowLevelContraption(IMyLogger ); // и даже такого не надо делать  
.....
}

я бы склепал примерно так
class MyLowLevelContraption
{
  MyLowLevelContraption();
  delegate void LogEventHandler(string);
  event LogEventHandler WriteLog;
.....
}

//зависимость отвызявается снаружи
new MyLowLevelContraption() { WriteLog += currentContext.WriteLog; }
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.