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

    image


    Это продолжение текста про архитектуры интерпрайз-систем. Рассуждения это хорошо, но какой в них толк без практического применения. Я покажу свой фреймворк в деле.


    Всё началось с того, что я рассказывал про проблематику проектирования приложений на .NET и ныл про нелёгкую жизнь в кровавом интерпрайзе. Затем я описал решение, которое сам придумал и реализовал — Reinforced.Tecture. То была теория, концептуальные рассуждения, визионёрство и снова нытьё. На этот раз о том, что на дворе 2020 год, а HKT в C# так и не завезли.


    Сегодня я продемонстрирую свой подход в действии на примере простенького проекта и покажу профиты, которые он даёт: от сокращения количества кода до автоматизации тестирования и оригинального подхода к документации. Как советовал старина Торвальдс: "Болтовня ничего не стоит, покажите мне код".


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


    Нам понадобится:


    • Простая сущность. В голову сразу приходят продукты и заказы. Пусть будут продукты;
    • EF-ный DbContext и локальная база данных;
    • Игрушечная бизнес-логика;
    • Простенький web-проект. Всё чин по чину, ASP.NET Core, WebAPI. В него логику и воткнём.

    Подготовка


    Структура проекта будет такая:


    image


    Я подключил EF.Core к сборке Data, закинул туда DbContext и glue-код для миграций. Потому что хочу оставить логику на .NET Standard и не тащить EF с собой.


    Кстати, интересно

    Обычно сущности кладутся в сборку с DAL-ом, рядом с контекстом. Здесь же зависимость развёрнута в обратную сторону — сборка с контекстом знает о всей логике. Это кажется контринтуитивным, но на самом деле вполне нормально для Tecture. Не стоит этому удивляться.


    Поведение Tecture мы будем смотреть на примере работы с продуктами. Вот его сущность, а логика вокруг неё будет простая и очень глупая:


    image


    Код DbContext-а абсолютно шаблонный, спрячу-ка я его под спойлер. В рамках тестового проекта я кладу болт на управление строкой подключения и тонкую настройку контекста — это сейчас не важно. EF я использую как рантайм, не более. Он не оказывает влияния на логику и вообще его поддержка подтягивается из отдельного пакета. EF для меня — удобный инструмент реализации ORM-аспекта. Но моя логика при этом остаётся чистой и свободной от сопутствующих EF-зависимостей.


    AcmeDbContext

    image


    Между делом я сделал базу данных в локальном инстансе MS SQL Express. Это первое что попалось мне под руку — окружение уже было настроено на моей домашней машине. EF.Core более-менее поддерживает и остальные базы — вроде MySQL и PostgreSQL. Однако, на уровне аспекта, где работает Tecture, становится совершенно пофигу. Просто мне так удобнее проводить демонстрацию. Никаких скрытых зависимостей от базы данных тут нет.


    Короче, пора втащить в логику первую зависимость. Докинем в неё Reinforced.Tecture и Reinforced.Tecture.Aspects.Orm.


    image


    Я рассказал про каналы в предыдущей статье и вот они мне пригодились. У меня будет простой канал для базы данных с единственным аспектом, отвечающим за O/RM:


    image


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


    image


    Интеграция


    На этом этапе проект уже вполне рабочий. Осталось только присоеденить Tecture к желаемому end-user решению. В нашем случае это web-проект. Я открываю его, иду в Startup.cs, и ищу там метод ConfigureServices. Это внутренний DI-контейнер, который идёт в комплекте с ASP.NET MVC. Для демонстрационных целей он вполне норм, поэтому я заталкиваю в него AcmeDbContext:


    image


    Теперь самое время поставить рантайм Tecture для взаимодействия с базой через EF. Прямо в web-проект. В моём рантайме реализованы 2 аспекта: O/RM и DirectSQL. DirectSQL я не буду здесь демонстрировать, но он есть. Как я уже сказал, в сборке с бизнес-логикой рантайм не нужен. Он должен подключаться только в ту часть системы, где бизнес-логика непосредственно вызывается, что сильно облегчает dll-ку с логикой на предмет зависимостей. Сам же рантайм вполне может реализовывать несколько аспектов за раз. Это не запрещено и — опять же — экономит зависимости:


    image


    Теперь надо засунуть Tecture в контейнер. Это делается через фэктори метод. Для наглядности я вынес его отдельно и снабдил комментариями. Тут я просто извлекаю из контейнера AcmeDbContext, заворачиваем его в LazyDisposable (это гибрид Lazy и Disposable, как следует из названия) и скармливаем его рантайму. Далее, для каждого канала мы указываем что вот именно этот контекст EF и надо использовать. Делается это с помощью входящих в комплект поставки рантайма fluent-методов:


    image


    По задумке такой интеграционный код пишется только один раз и не меняется примерно никогда. Принцип "настроил и забыл" в действии. Для сложных многоканальных систем, конечно, можно нагородить всякие обёртки над построителем Tecture, чтобы настраивать его на работу с разными комбинациями внешних систем, но в нашем приложении такое пока не нужно. Тут мы видим separation of concerns: пусть лучше действительно сложный и чувствительный кусок системы будет краток, прост, читаем и — главное — написан один раз. Далее, на ваше усмотрение — можете вынести его в отдельный репозиторий, единожды собрать и просто использовать.


    Вообще я стараюсь возложить максимум ответственности на рантаймы и аспекты. По сути я выношу в них всю реальную проектировочную работу, которую необходимо сделать в приложении. По задумке, обслуживать это дело должен системный архитектор. Я полагаю, в любой компании найдётся бородатый разработчик, который сможет один раз в этом разобраться, настроить и больше никогда не трогать. Но если такого нет — можно воспользоваться моими аспектами и моим рантаймом. Таким образом, пресловутые separation of concerns я поднимаю на организационный уровень.


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


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


    Как бы то ни было, теперь можно использовать ITecture из контроллеров. Давайте поиграем с ним.


    Запросы


    Я хочу написать совершенно тупой и очевидный веб-метод, который будет отдавать нам продукт по Id. Держу пари, у всех такой есть. И у всех на такой случай есть DTOшка. Вот, например, моя:


    image


    Теперь мы идём в контроллер, прокидываем ITecture в конструктор, выдёргиваем его в отдельное поле, через которое получаем долгожданный доступ к методу From<>. Используем его для того, чтобы вытянуть продукт по Id и смапить на DTO-шку:


    image


    И на этом, в общем, всё. Таким способом можно писать все методы для возврата всех сущностей по Id с DTO-шками без единого репозитория. Можно дать волю фантазии и не стесняться использовать компилятор C# на полную. Скажем, возврат множества DTO-шек можно обыграть таким способом:


    image


    image


    Или таким:


    image


    image


    Можно даже фигачить расширения непосредственно для IQueryable, дёргая их из читального конца канала через метод All<>, предоставляемый аспектом. Тут действительно можно дать фантазии развернуться. Хочешь — делай развесистый построитель запроса вокруг конца канала, со своими промежуточными абстракциями. Хочешь — строй фасады к AutoMapper, используя его копирующие expression-ы. Всё это в любом случае — статика без контекста. Она легко покрывается тестами при желании, не требует базы данных и сборки контейнера. Можно даже сделать свой аспект и выкинуть туда всё, что касается запросов. Короче, бескрайнее поле для творчества и самореализации. Я же не буду в нём зависать, потому что впереди ещё много интересного.


    Операции


    К сожалению, продуктов у нас в базе нет и что-то запрашивать из неё бесполезно. Нужен способ заслать туда данные. Как я уже говорил, добавление в Tecture делается через сервисы. Вот я и сделаю сервис для работы с продуктами. За полсекунды, я набросал вот такую заготовку вот в этом месте:


    image


    Я уже говорил, что в сервисах есть тулинги и они очень удобны. Я хочу остановиться и прям показать как они работают используя анимацию. Конкретно эти тулинги взяты из ORM-аспекта — зацените механику:


    image


    image


    Кстати, нам понадобится Id свежедобавленного продукта. Когда я делал аспект ORM — долго думал как это обыгрыть. Ведь создание сущности — это команда, а получение Id — запрос. Как быть? Я выкрутился: команда Add экстендит интерфейс IAddition<>. После логической операции, саму команды можно смело вернуть из метода логики как IAddition<Product>. А уже после сохранения скормить аддишен методу Key читального конца канала. Это даст нам вожделенный Id. Но надо ещё указать где именно у сущности первичный ключ. Я реализовал эту механику через интерфейс IPrimaryKey<>. Вот:


    image


    Готово. Теперь можно вернуться в контроллер и наконец-то призвать наш сервис:


    image


    Postman сказал мне что это работает и вернул Id нового продукта.


    Теперь у нас есть маленькое бизнес-приложение, сделанное на Tecture. Мы уже получили чёткую грануляцию проекта, удобные примитивы и минимум кода, но это только начало. Сейчас я перейду к самому интересному — к конкретным преимуществам, которыми меня щедро осыпает такой подход.




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


    Я стараюсь быть практичным и всегда считал что архитектура решения сильнее всего влияет на цену поддержки и развития. Ещё в университете на парах по управлению проектами мне показывали статистику запоротых разработок в американской авиаиндустрии. Я чётко помню что в 90% случаев провал определялся ошибками на стадии раннего проектирования и управления требованиями. Это наталкивает на мысль, что если сначала думать, а потом делать — то можно не только предотвратить наступание на грабли, но и драматически снизить стоимость поддержки.


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


    Я убеждён что только очень меркантильный человек мог придумать следующие фичи.


    Трейсы


    Вернёмся к коду контроллера. В Tecture есть неприметные методы BeginTrace и EndTrace. Я окружаю ими содержимое экшона по периметру. Вот так:


    image


    Помимо них тут есть вызов Explain. Так я прошу Tecture объяснить мне что происходит между началом и концом трассировки. Втыкаю точку останова на return и запускаю:


    image


    Бам! Человеческим языком мне рассказали что конкретно произошло в логике. Само собой, тут и логика-то из трёх с половиной действий. Но когда проект обрастёт сервисами, тонкой нюансировкой, зависимостями и прочими сложностями, которые порождает бизнес — трейс всё так же будет объяснять что прочитано и записано в базы, очереди, кэши, файлы. И если мы не будем лениться, а приложим совсем немного усилий добавляя командам и запросам аннотации, то текст трейса станет ещё более осмысленным:


    image


    Запросы аннотируются методом .Describe.


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


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


    Tecture предлагает пойти другим путём: прибить описание происходящего к командам и запросам. Дать маленькое пояснение к каждому конкретному действию. Это сверх-дёшево, занимает считанные секунды и устойчиво к перестановке кусков кода с места на место. Потом фреймворк сам сложит эти описания вместе и красиво, последовательно и предельно понятно расскажет что же всё-таки случилось, когда вы нажали ту мелкую кнопку в углу экрана. К тому же, это композабельно. То есть если один метод документирован через аннотации, то его вызов будет выдавать эту документацию где бы вы его ни использовали, позволяя оценить происходящее в динамике и поделиться этим знанием с товарищами. Knowledge management!


    Ещё есть интерфейс IDescriptive, который можно реализовывать, скажем, у сущностей. Он нужен чтобы вместо "User entity" у вас выводилось "User Vasiliy Pupkin". Это сделает трейс совсем похожим на человеческий текст и если звёзды сложатся, то он будет пригоден для обсуждения с заказчиком. Гораздо удобнее планировать изменения в системе, когда все понимают что она делает.


    В прошлой статье я говорил, что по сути бизнес-логика отдаёт команды внешним системам. Explain трейса — это отчёт с авторскими комментариями о том, какие внешние системы были затронуты, что у них запрошено и что им отдано. Это ли не киберпанк, который мы заслужили: приложение, которое само является источником информации о себе независимо от изменений требований.


    Но это ещё не всё.


    Захват данных


    Трейс включает в себя глубокие копии всех ответов на все запросы. Эту информацию можно извлечь в любую структуру данных и я распоряжусь ей элегантно. Докинем в web-проект ещё один пакет: Reinforced.Tecture.Testing. Он тяжеловат — по зависимостям тянет за собой Roslyn. Не надо так делать в живых приложениях, но я это сделаю исключительно в демонстрационных целях. И вот для чего:


    image


    Этот пакет добавил трейсу 2 эктеншона. GenerateData и GenerateValidation. Нас интересует первый метод, который проще всего вызвать:


    image


    Смотрите: все ответы на запросы, которые произошли в этом куске логики сконвертились в C#-класс. Я просто нажал пару кнопок, а Tecture уже подготовил мне fake-данные для тестирования. Мне не надо вбивать их руками, не надо подбирать, ставить, и настраивать фейк-генератор, не надо использовать сервисы в духе Mockaroo. У меня уже есть девелоперская база с какими-то данными — я на ней проект дебажу. Для тестов мне придётся эти данные хардкодить, так почему бы не автоматизировать этот процесс?


    Но это полдела. Есть ещё один метод из Reinforced.Tecture.Testing. Его вызвать гораздо сложнее, на целых 4 строчки:


    image


    На пальцах: есть бизнес-логика, она генерирует цепочку каких-то команд при определённых вводных (данных из внешних систем + пользовательский ввод). Данные из внешних систем мы уже дампнули в файл с кодом. Значит и команды тоже можем!


    Я срезаю углы и минуя все промежуточные форматы, сразу генерю из очереди команд последовательность проверок. В форм-факторе огромной функции-предиката. Разумеется, уровень дотошности проверок можно регулировать.


    А всё для того, чтобы...


    Unit-тесты


    Положим, по нашему куску логики достигнут консенсус относительно корректности. QA и заказчик в один голос кричат: "во, оставь, сейчас работает как надо!". После чего мы берём этот кусок логики, запускаем в тестовом окружении, выдираем данные и валидацию, а потом запечатываем это добро в регрессионный unit-тест.


    Настройка CI/CD пайплайна под подобные тесты — дело пары минут. Не надо понимать окружение, базы, кэши, очереди. Не надо ждать пока они запустятся и прогреются. И подчищать их после запуска тоже не надо. Тесты, построенные на захвате данных Tecture и его валидации самодостаточны и запускаются прямо из коробки без всего. И с блеском решаю задачу контроля регрессии: если кто-то меняет логику, эти тесты послушно падают. При том падают тоже композабельно — по всей цепочке связанной функциональности. Цепью красных шариков можно проследить какие части системы похерены.


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


    И моки здесь совершенно не нужны. А значит не надо прятать сервисы за интерфейсами. И городить репозитории для запросов тоже не надо.


    Дело за малым — подобрать тестовую инфраструктуру, чтобы удобно обыграть вызовы GenerateData и GenerateValidation. Тут я ничего конкретного не предлагаю и в NuGet не публикую. В тестовом проекте я сделал вот так, просто для примера.


    В частности, с этой инфраструктурой можно писать тесты таким вот изящным способом:


    image


    image


    А вот что я делаю, когда логика под тестами меняется:


    image


    Таким образом, я трачу на написание осмысленных unit-тестов от силы по 5 минут своего рабочего времени. Это чистый, концентрированный профит.


    Остаётся только добавить, что автогенерированный код можно править руками (относясь с осторожностью при перегенерации). Так-то я не планировал полностью вытеснить обычные unit-тесты. Но автогенерация, собака, эффективная, а я слишком ленивый чтобы делать что-то ещё.


    Такие вот дела.


    Пост-скриптум


    На разработку и продумывание этого подхода у меня ушёл год с небольшим. Огромное спасибо всем моим коллегам из разных стран, которые подкидывали мне идеи и рассказывали про беды своих проектов. Я надеюсь, что получилось довольно продуктивно и интересно.


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


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


    Пакеты опубликованы, исходники есть, я на связи в твиттере, телеграме и на github. Если вам вдруг хочется пополнить ряды early adopters и взять Tecture для своего пет-проекта — напишите мне, я постараюсь помочь.


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


    Успехов!

    Средняя зарплата в IT

    110 500 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 7 138 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 26

      +5
      У меня один вопрос только. Извините, не по теме статьи)

      Заглавная картинка в виде кода на черном фоне вам досталась по наследству от fillpackart? Он как раз поменял оформление статей.

      Ну просто очень уж бросается в глаза стилистика оформления статьи. Не, может это только мне так кажется. Но прям бросается в глаза.
        +3

        В конце статьи я прямо выражаю благодарность fillpackart и его сообществу за помощь с редактурой и оформлением. Идею картинок с кодом в качестве иллюстраций я тоже украл у него. Только он любит Rider, а я — Visual Studio

        +3
        А за «интерпрайз» не стыдно? :)
          0

          Это кринж :) Он делается чтобы было стыдно читателю.

            +3

            Странно, почему мне должно быть стыдно за то, что написали вы? Мне, например, всё равно, как вы напишите — "интерпрайз", "энтерпрайз" или "enterprise". На какой круг читателей должен воздействовать этот ваш кринж, если не секрет?

              +5

              Не секрет: это должно воздействовать на круг чрезмерно серьёзных читателей, которые потребуют от меня маркетингового буклета про мой фреймворк, сравнительного анализа подходов, метрик, планов по интеграции в существующие приложения, примеры success story на живых проектах и сильно разочаруются, когда узнают что всего этого пока ещё нет.


              Разработка сейчас медленно перекатывается из стадии "proof of concept" на стадию "early adopters" и вкладываться в вышеперечисленные вещи ещё не время.


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


              Как-то так. Извините, если грубовато.

                +2

                Спасибо, теперь мотивация понятна — заголовок написан для тех, кто не будет читать статью (y)

                  +1

                  Всё так, да.

          0
          Спасибо за статью!
          Один коммент по оформлению — гифки выглядят очень наглядно но их нельзя поставить на паузу. Можете ниже прятатать под спойлером скрин с финальным вариантом?
            0

            Учту на будущее, спасибо :)

            0
            Ссылка, которая, кажется, должна вести на предыдущую статью, ведёт на эту же страницу.
              +1
              Не нашел исходников описываемого тестового проекта. Было бы здорово добавить их в репозиторий с библиотекой.
                +1

                Сделаю и добавлю отдельно. Просто я ж гифки записывал, там немного "декорации" есть :)

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

                Как бы не получилась такая ситуация — бизнесс-логику стало просто писать, хватит одного мидла, но ему нужны готовые аспекты и каналы. А их пишет только вон тот опытный бородач. И не успевает. Надо два таких, нет, лучше три. А то тут кровавый интерпраиз и новая система для интеграции, снова.
                Вы ведь сами признаете проблематику:
                Работа с внешними системами — это краеугольный камень разработки бизнес-приложений… Откройте свой рабочий проект, найдите то, что у вас называется «бизнес-логика»: это просто код, который сам ничего не делает, а только говорит что делать внешним по отношению к вашей системам. Какие-то действительно сложные вычисления руками в рамках формошлёпства — скорее исключение, чем правило.

                Бородачу наверно не интересно писать одни аспекты. Компании неинтересно нанимать кучу бородачей за большие деньги, но выбора нет, мидлы написание аспектов не потянут. Или потянут?
                Вообщем, это хотелось бы понять из вашего эксперимента:
                Сейчас мне на работе выделили небольшой кусок системы, чтобы обкатать Tecture. Не хочу говорить ничего раньше времени, но по результатам будет обзорная статья.

                Вы ведь не в одиночку будете это обкатывать?
                  0
                  А их пишет только вон тот опытный бородач

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


                  Или потянут?

                  Я сделаю всё от меня зависящее чтобы потянули. Сейчас немного разгребусь с текучкой и напишу детальные пошаговые инструкции-чеклисты по написанию аспектов и рантаймов. Мне сложно оценить сложность такой задачи — как по мне она довольно лёгкая и творческая. Плюс аспекты вполне могут кочевать из проекта в проект — почему нет?


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


                  Вы ведь не в одиночку будете это обкатывать?

                  С коллегой :)

                  +3
                  Трэйс прямо божественен! Я реализовывал примерно похожее внешне поведение, но для логгирования действий в админке — с человеко-понятным описанием, кто, что, где и с чем делал(просмотры, редактирования, удаления) — но у меня этим из за ограничений архитектуры обмазаны все контроллеры, а тут прямо из коробки идеальный стэйктрейс вызова.

                  Автогенерация тестов тоже порадовала. Это прямо решает всю боль разрабов. Интересен, конечно, момент — насколько они получаются полные? Насколько покрывают корнер кейсы, особенно в случае с ветвистой логикой поведения из условных операторов? Такой код, конечно, «некрасивый» и «неправильный» — но будем реалистами — в энтерпрайсе он появится в любом случае.
                    +1

                    Спасибо за отзыв.


                    Трэйс прямо божественен

                    В имейте в виду только что в него попадают все действия с каналами. То есть совсем все. Будет у вас кэш — факт записи в него тоже свалится в трейс. Хотя в целом трейс вполне можно отфильтровать руками и сделать свой Explain, включив туда только то, что интересно.


                    Насколько покрывают корнер кейсы

                    То, что я делаю называется data-driven testing или table-driven testing. Такие тесты не покрывают совершенно все corner case-ы — вы должны сделать это сами, но в смысле не руками, а подбить правильные тестовые данные и вводные, чтобы у вас проверялись все ветки. Ну то есть условно регистрируйте в своей админке юзера с одними правами — пробуете из теста залогиниться и удалить файл, захватываете занные. Заводите юзера с другими правами — пробуете то же самое ещё раз, захватываете данные.


                    На реальных проектах вам наверняка с этим поможет QA. То есть корнер-кейсы за вас не откроют, но уже открытые помогут оформить.


                    идеальный стэйктрейс вызова

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

                      0
                      Мне тоже очень понравилась идея с description/annotation, может быть подумать о возможности дополнительных уровнях детализации. Типа по дефолту так, а если хочется уровень Debug то эдак. Надо только в случае Debug параметр сделать как lambda expression чтобы не вычислялось всё зазря, когда не требуется.

                      Ну и может быть «хэштэги», типа возможность добавить список тэгов, что позволит при анализе описаний делать какой-никакой фильтр.
                        0

                        Спасибо. .Annotate — extension-метод для команды (как и .Describe для запроса) — вы можете без труда ввести свою систему именования прямо поверх них.


                        по дефолту так, а если хочется уровень Debug то эдак

                        Можно использовать #ifdef

                    +1

                    Меня немного напрягает, что BeginTrace и EndTrace надо вызывать руками, очень легко один из этих вызовов забыть. Вы не думали сделать отдельный тип, который при создании вызывает BeginTrace и в Dispose вызывает EndTrace, чтобы трейс можно было использовать с оператором using и таким образом исключить возможность забыть вызвать EndTrace?

                      0

                      Я думал о том, чтобы добавить такую опцию, но забегался и забыл. Сделать такую обёртку нетрудно руками.


                      Но это в любом случае opt-in, потому что имея BeginTrace/EndTrace проще вычленять хитрые куски логики, когда например начало в контроллере, а конец — внутри сервиса до первого сэйва.

                        0

                        Хотел сказать, что ничто не мешает обёртку прокидывать руками, а потом понял, что Dispose придётся вызывать руками :( Всё-таки тяжело без RAII.

                          +1

                          Ещё есть using без скоупа с последних версий C#. Я вот в тестах использую такой. В общем есть много способов обыграть, аж глаза разбегаются. И это главное. :)

                      0

                      Несмотря на то, что статья (и вся серия) это якобы про бизнес-логику по факту статья о работе с базой. И я не чтобы прям топлю за DDD, в котором рисуешь домен, который ничего не знает о Persistence или принижаю значимость базы данных (подавляющее большинство enterprise вокруг БД крутится, чаще всего одной из тройки реляционных), просто глянул в комментарии к прошлой статье о том, что обёртка есть только для одной БД и даже не особо умеет plain sql типа как в Dapper.


                      И вся логика начинается с фразы From<Db> — ну это понятно, что вроде как это "канал" и вроде как можно иные каналы описать… но по факту это просто иная разновидность EF, причём там уже под разные SQL базы есть неплохая кодовая база (хотя то, как реализована mysql мне лично не особо нравится). И она может быть в чём-то лучше, чем EF, в чём-то просто "иначе" ("привыкнешь")


                      А допустим, если сказать "очереди СМЭВ" — и это уже будет кагбэ за рамками статьи? Тоже вроде как "логика".

                        0

                        Всю первую часть предыдущей статьи я рассказывал, что канал может абстрагировать любую внешнюю систему. Странно, что вы увидели только БД. Тем более странно, что вы увидели только одну БД, когда я говорю что в качестве рантайма используется EF.Core, который поддерживает как минимум три базы.


                        И вся логика начинается с фразы From<Db>

                        С From<Db> начинаются только запросы и только к базе данных. Только к той, которую вы привязали к каналу Db.


                        это просто иная разновидность EF

                        Мимо. Обёртка для DirectSQL, кстати, есть.


                        очереди СМЭВ

                        Делаете аспект СМЭВ, подключаете его к любому каналу и погнали.


                        В остальном — см. вот этот комментарий и мой ответ на него.

                        +1
                        Ведь создание сущности — это команда, а получение Id — запрос.

                        По моему личному опыту, строго разделение на «команды» и «запросы» — довольно иллюзорно. Для команд рано или поздно (скорее рано) появится необходимость возвращать больше информации в ответе. Для запросов выяснится, что надо попутно записать информацию в аудит или счетчики, т.е. это уже будет не совсем чистый запрос.

                        _tecture.Save()

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

                        Будем подождать 3ю часть этого сериала, пока интригующе, спасибо! :)

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

                        Самое читаемое