Вторая попытка рассказать про Tecture здесь. В первый раз я был сильно уставший, на меня наехали джависты на хабре, я был на нервах и попытался сумбурно изложить идею. Предсказуемо написал чушь, скопипащеную из своего же ответа джависту на том же хабре чтобы уложиться в ограничение по длине. На самом деле очень благодарен Васе, что он всё удалил и своевременно уведомил меня что я упорот, спасая таким образом мою жопу неминуемого позора.
Сейчас меня попустило, я каюсь и наконец могу адекватно рассказать что это такое. Итак.
Reinforced.Tecture - это экспериментальный архитектурный фреймворк для C#. Моё личное видение того, как надо писать кровавый ынтерпрайз в мире .NET в 2к20 с минимальным количество боли и максимальным количеством профита.
Все знают что такое кровавый ынтерпрайз: опердни, автоматизации документооборота, складов, отчётности, делопроизводства и прочая нудятина с префиксом "бизнес-" и привкусом офисной затхлости. Обычно такой код пишется двумя способами:
- непристойно обмазывается классическими ОО-паттернами родом из Java (да, даже на C#) и лихих 80х-90х. Как пример - MS-овский eShopOnContainers, где авторы попытались применить вообще всё, что когда-либо слышали про ОО-дизайн;
- или говнокодится на коленке не самыми квалифицированными сотрудниками в тщетной попытке уложиться в дедлайны и удовлетворить бизнес на предмет требований. Как следствие - генерит больше мемов, чем полезной работы.
Первое вроде как считается "правильно", второе вроде как считается "быстро". Тут я и подумал: а почему никто не сделает чтобы было быстро и правильно? C# - язык с крутыми языковыми плюшками, довольно мощной системой типов и в то же время вполне себе интерпрайзный, что позволяет щедро использовать его в кровавом продакшене в отличие от oCaml и прочих хаскеллей. К тому же я очень люблю и хорошо знаю C#, поэтому взялся за проектирование.
Первые робкие попытки начались когда я работал из Новосибирска на эстонскую Replace Group в направлении складского автоматизирования. У нас был стартаперский дух, необходимость фигачить по килограмму фич в неделю и в то же время довольно сложные логические требования к функциональности, упиравшиеся в евробухгалтерию одним концом. А я уже тогда был шарповик-затейник и мои проектировочные инициативы, сокращающие количество кода и повышающие надои с радостью принимались и весьма ценились.
Потом контракт с эстонцами закончился и с разрешения начальства, я забрал свои архитектурные наработки с целью их систематизировать и допилить до чего-либо вменяемого. И вот спустя полгода работы в spare time, у меня наконец имеется продуманный и законченный авторский архитектурный шаблон со всем необходимым glue code для шарповых приложений. Хоть прям сейчас запиливать автоматизацию похоронного бизнеса.
Получилось довольно своеобразно, в духе киберпанка и не без доли кринжа. Чем-то напоминает MediatR, EF.Core и Autofac в одном лице. Некоторые функционально-осведомлённые товарищи углядели в этом ZIO (а я даже не понял о чём это они лол).
Если рассказывать крупными мазками, то набор плюшек такой:
Решение выполнено в общей идейной стилистике Microsoft: с оглядкой на функциональное программирование, но без фанатизма, монад функторов и повальной иммутабильности;
За основу взята концептуальная идея разделения запросов и команд (модное нынче слово CQRS);
Команды складываются в очередь и диспатчатся опосля. Я использовал тонны синтаксического сахара - только бы не заставлять пользователя писать дурацкое слово "Enqueue" и создавать инстансы команд руками;
Запросы пишутся в виде статических расширений. Я очень хотел убить Repository pattern - я его убил;
Сервисно-ориентированная архитектура обыгрывается на свой слегка кринжовый лад: для сервисов не надо писать свои интерфейсы, а надо сразу классы. Кринжовость заключается в том, что у этих классов должен быть приватный конструктор. Патамушта!
В решение вмонтирован свой мини-DI-контейнер, обслуживающий инстансы сервисов. Только он убер-простой: нет лайфтайм менеджмента (потому что лайфтам сервисов логики всё равно известен), не требуются портянки регистраций (все метаданные берутся из типов). Пишешь
Do<OrdersService>()
и готово;Из коробки поддерживается работа с бесконечным количеством баз данных одновременно. И от всех вы абстрагированы;
Анемичные сущности, которые принято фанатично закрывать на запись почти ото всех кроме некоторых сервисов. Таким образом форсируется грануляция и сокращается количество мест, откуда может прилететь неожиданное поведение системы;
Система трейсинга. Трейс в Tecture - это не просто лог. Это - запись всех отправленных запросов и всех созданных команд. По вызову магического метода трейс рассказывает вам человеческим языком что конкретно делает приложение в том или ином случае.
В огромных проектах, когда все уже забывают какие были реквайрменты эта фича должна по задумке стать божественным спасением утопающих. Когда тикеты потёрты, емейлы потерялись, документы истлели, Confluence лежит а комментарии в коде встречаются нечасто - то система, готовая сама объяснить что в конечном счёте улетает в базу данных по нажатию той или иной кнопки - буквально восьмое чудо света. А если ей ещё и немного помочь, оставляя короткие аннотации к командам и человеческие описания запросов - то текст трейса приобретает характер серьёзного документа. На случай важных переговоров, как говорится.
В нашей эстонской команде этой фичи очень не хватало, потому что в управление требованиями мы в конечном счёте и упёрлись.
В трейсе сохраняются клоны ответов на все сделанные запросы. Поэтому нажатием другой волшебной кнопки эту информацию можно выдернуть в отдельный файл и наречь "тестовыми данными";
В трейсе сохраняется очередь команд, поэтому нажатием третьей волшебной кнопки, их можно так же выдернуть и сгенерировать код, который верифицирует что очередь команд именно такая;
Комбинируя две вышеописанных фичи и ловким движением укладывая их в unit-тест получаем доселе невиданную плюшку архитектуры: автоматизация автоматизации тестирования. Т.е. если у вас есть система, про которую бизнес говорит "на данный момент всё работает как надо" - ленивым движением пальца вы можете сгенерить регрессионные тесты, реально валидирующие логику и не требующие поднимать вообще никакой инфраструктуры для прогона. DevOps-инженер уже ждёт куда бы впаять вызов
dotnet test
.В виду специфической природы тестирования - отпадает необходимость в моках, абстрактных прокладках, промежуточных интерфейсах. Можно просто брать. И. Пилить. Фичи.
Стоит ли говорить что логика и запросы полностью композабельны. Сервисы можно использовать друг из друга, одни запросы из других;
В одной из фич легализован прямой SQL в базу данных. Ура товарищи! Теперь пуляться SQL-строками можно безопасно и подконтрольно;
Есть точки расширения для поддержки новых и новых фич.
В общем-то, я думаю что пока достаточно. Документация с недавних пор лежит в wiki репозитория - там можно зайти и подивиться как оно работает.
Очень круто звучит, нашел в статье проблемы с которым сам часто сталкиваюсь. Но совершенно непонятно как вкатится, если научен "обмазывается классическими ОО-паттернами" и тем более начать использовать в проекте старше 5 лет. Придется отложить до светлого будущего, когда нужно\можно будет начать что-то с нуля.
Энивей респект.
Сорь за коммент не по теме.
Мб у меня клюк, но когда эмблема маленькая (https://prnt.sc/ubz8hs), она будто создана для обозначения диктаторского режима.
Мб это из-за контекста, рядом белое а тут выделается)
Я в своих навыках пока крайне далек от архитектурных вопросов, но с удовольствием прочитал цикл статей на хабре и подход мне понравился (и всякие фишки типа трейса - это ж грааль для любителей разработки через отладку вроде меня).
Парочка вопросов:
Как продвигается тестирование на рабочем проекте? Как вообще отнеслись коллеги, есть ли обратная связь от авторитетных людей в индустрии?
И ещё вопрос по аспектам. Я правильно понимаю, что на всякие сильнораспространённые штуки можно написать свои аспекты и куда-то выложить, в тот же NuGet, так что со временем (в идеале) появится коллекция аспектов под всё самое необходимое?
Удачи тебе с проектом, выглядит круто!