Pull to refresh

Comments 246

Например, внезапно, вы решите использовать вместо Postgresql MongoDb, или вообще файлики, или использовать mock’нутые данные, операции с которыми будут производится в памяти. И при некоторых условиях — это может заставить переписать почти всю логику.

Чтобы таких ситуаций не возникало, мы можем использовать некоторые механизмы, которые будут максимально далеко отодвигать время принятия решения. Один из этих механизмов — абстракция.

Угу, абстракция, ага. А теперь давайте посмотрим на этот конкретный пример: вот вы хотите сделать такую систему, в которой не важно, что вы используете — СУБД, или внезапно ДОБД, или внезапно, ключ-значение (я бы хотел, конечно, посмотреть, на проект, в котором это происходит внезапно). И за какой же абстракцией вы спрячете все эти различия, чтобы замена произошла незаметно и безболезненно?


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

Полностью согласен с вами, не всегда можно просто сменить способ хранение данных. Имеется в виду скорее переход с однотипных баз данных, например c Postgresql на MSSQL и т.д.

Даже в случае key-value, я думаю, это нужно инкапсулировать доступ к данным так или иначе.

Ну то есть первый же попавшийся мне на глаза пример, который вы показываете, как пример "хорошей архитектуры", невозможен.


Так какие же полезные (и при этом не-тривиальные) советы дает в этом вопросе книга?

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

Так чему же тогда учит эта книга? "Думать"?


Как минимум совет инкапсулировать доступ к данным через какую-нибудь абстракцию — рабочий

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


такого рода архитектурные решения нужно принимать как можно позже

А код-то как писать, если не принимать такие архитектурные решения рано?

Это проходит по категории «тривиальный». Чтобы он стал не-тривиальным, нужно иметь критерии, отличающие «хорошую» абстракцию от «плохой».

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

А код-то как писать, если не принимать такие архитектурные решения рано?


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

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

Len Bass, Paul Clements, Rick Kazman. Software Architecture in Practice, если я ничего не путаю за давностью лет.


Ну в моем понимании, ваше приложение должно иметь какую-то свою логику (бизнес логику), и его нужно начать именно с ней

И как предлагается начать с бизнес-логики, если она работает с данными из БД и других внешних источников, своим входом имеет UI, и выходом — тоже UI?

За авторов спасиб)

И как предлагается начать с бизнес-логики, если она работает с данными из БД и других внешних источников, своим входом имеет UI, и выходом — тоже UI?

сейчас скажу в контексте только бэк-енда. Есть такая книжечка про microservices & DDD от майкрософтов, возьму пример их же проект EShopOnContainers, сервис Ordering. В домене есть некоторые Aggregate и Entities. Можно начать с них, обдумать что как и с чем должно взаимодействовать, описать логику, а в самом поведении использовать абстракцию Repository без implementation. Как-то так. Если прям нужно тестить — ну можно in memory implementation в IoC подставить. Можно так же заняться Application Layer.
а в самом поведении использовать абстракцию Repository без implementation.

Стоп. Вот же оно ваше "архитектурное решение", использовать паттерн repository, да еще и с конкретным интерфейсом наверняка. А вы говорите "отложить на потом". Да, а потом вы возьмете СУБД, и поймете, что ее нельзя запихнуть в тот интерфейс, который вы на первом этапе нарисовали для репозитория, и что тогда?


Можно так же заняться Application Layer.

А что такое в этом контексте application layer, и почему его написание не приводит к принятию архитектурных решений?

Стоп. Вот же оно ваше «архитектурное решение», использовать паттерн repository, да еще и с конкретным интерфейсом наверняка. А вы говорите «отложить на потом». Да, а потом вы возьмете СУБД, и поймете, что ее нельзя запихнуть в тот интерфейс, который вы на первом этапе нарисовали для репозитория, и что тогда?


Я думаю, что есть организация данных — которая сама по себе навязывает способ хранения данных. Сам я, к примеру с NoSql не работал, но мне кажется, что в том конкретном случае логично понятно, что все связи в сервисе было бы проще организовать с помощью SQL-based структуре хранения данных. И если например бизнес логика навязывает один способ хранения данных как подходящий и иной — как не подходящий, может стоит использовать подходящий, а не менять бизнес логику из-за того, что работа с данными в них не вписывается?

Что в таком случае бы вы сделали? Вы предлагаете от способа хранения данных плясать и писать бизнес логику?

А что такое в этом контексте application layer, и почему его написание не приводит к принятию архитектурных решений?


Это уже DDD штука, если в двух словах — Layer, который в себе имеет Use Case'ы, делегирует все запросы с UI на Домен, и обратно и т.д.
Я думаю, что есть организация данных — которая сама по себе навязывает способ хранения данных.

Так, и? Что с решениями-то делать?


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

"Логично понятно что" — это вы приняли (крупное!) архитектурное решение ("мы будем использовать реляционную модель") и даже не заметили этого.


И если например бизнес логика навязывает один способ хранения данных как подходящий

Подождите, вы же в статье пишете "БД — это детали реализации".


Вы предлагаете от способа хранения данных плясать и писать бизнес логику?

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


Это уже DDD штука, если в двух словах — Layer, который в себе имеет Use Case'ы, делегирует все запросы с UI на Домен

А у вас Use Case никак не зависят от того, каким образом используется домен? Проще говоря, use case для человека перед пользовательским интерфейсом, и для внешней системы, работающей через API, не отличаются?

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

В примере конкретно Майкрософтов, DDD навязывает некоторые решения, как, к примеру, о интерфейсе репозитория — потому что, это — паттерн, который и был создан для абстрагирования от доступа к данным. И он имеет свой определенный интерфейс.

Рассмотрим к примеру апдейт того же самого ордера. В репозитории. Для этого есть соответственный метод на GetOrder(), и если его не будет, то и апдейта не будет, и если способ хранения данными не позволяет достать этот ордер — то значит он не подходит. То же самое с методом репозитория на сохранение всех изменений, который призван для того, что бы в случае изменений не одной ентити, а нескольких в пределах одного агрегата — сделать это одной транзакцией (либо все, либо ничего). Это, как я понимаю. реквайрмент бизнеса, и если же такой метод не может быть представлен каким-то способом хранения данных, значит он не подходит для этой бизнес логики. Именно поэтому ДДД и говорит о том, что полного соответствия ентити в БД с ентетей из домена может и не быть.

Так, а кто говорил, что эта книга подходит под все, и волшебная таблетка от всего.

А для чего она подходит и от чего она помогает?


DDD навязывает некоторые решения, как, к примеру, о интерфейсе репозитория — потому что, это — паттерн, который и был создан для абстрагирования от доступа к данным

Я про это и говорю: вы, на самом деле, принимаете множество архитектурных решений на самом старте (начав, кстати, с решения исползовать DDD). Так что же, собственно, вы предлагаете отложить на потом?


Рассмотрим к примеру апдейт того же самого ордера. В репозитории.

А почему в репозитории-то? Почему вы другие паттерны не рассматриваете?


Это, как я понимаю. реквайрмент бизнеса

Неа, это то, как вы это какое-то требование бизнеса интерпретировали. Совершенно не факт, что бизнесу нужна именно такая транзакционность, как вы описываете.


Именно поэтому ДДД и говорит о том, что полного соответствия ентити в БД с ентетей из домена может и не быть.

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

Подождите, сначала вы говорите, что способ хранилища данных должен полностью соответствовать требованиям бизнеса, а потом вы говорите, что именно поэтому «полного соответствия быть не может». Где-то что-то перепутано.

Не много не так выразился: может допускаться, что будут различия между моделью БД и моделью Домена. Это делается, на примере того же EShopOnContainers, как раз для абстрагирования от деталей хранилища. У ордера(может и не у него, но не суть, в какой-то ентите) должен быть адрес. И этот адрес мы можем сразу организовать как член класса этого самого ордера, хотя прямой связи может и не быть в SQL(так оно и есть, ибо там это Value object), или же мы сможем с легкостью сделать по классике, через ещё одну таблицу в SQL и реляционную связь, либо же использовать NoSQL и поместить адрес прям в ордер. Можно вообще организовать бинарные файлы, Но фишка вся будет в том, что интерфейс репозитория как возвращал ордер с адресом — так и возвращает — вот такой маленький (идеализированный) пример, понятно, что есть случаи, когда так легко сделать не получится, опять же говорю, я этого и не отрицал — книга — не панацея от всего.

П.С. про легкость перехода с SQL на NOSQL или key-value, напишу апдейт, тут с вами согласен — легким этот переход не сделать, но все же, думаю, что если это будет инкапсулировано и абстрагировано по максимуму — это сделает переезд менее болезненным. У меня нету большого опыта, что бы знать какие ситуации бывают, и насколько это сложно, ибо сам такого опыта не имел. Конкретные техники, и как лучше абстрагироваться в том или ином случае — это уже не об этом книга, на то архитекторы и получают свои тысячи зеленью и едят сыры xD
может допускаться, что будут различия между моделью БД и моделью Домена.

Может допускаться, да. И что? Как это помогает принимать решения?


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

Угу, и во всех этих вариантах у вас будут разные гарантии целостности. Дада, те самые, про которые вы только что говорили как про бизнес-требования на транзакции. Вас это не волнует, нет?


книга — не панацея от всего.

Вы это уже говорили, и я спросил: а для чего же она полезна?


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

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


это уже не об этом книга

А о чем она тогда? Вы вроде пишете "И вот как строить эту архитектуру". Нет?

Угу, и во всех этих вариантах у вас будут разные гарантии целостности. Дада, те самые, про которые вы только что говорили как про бизнес-требования на транзакции. Вас это не волнует, нет?

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

Может допускаться, да. И что? Как это помогает принимать решения?

это поможет какое-то время НЕ принимать решения.

Вы это уже говорили, и я спросил: а для чего же она полезна?


Ну как по мне набор базовых принципов, которых следует придерживаться что бы построить хотя-бы не неправильную архитектуру. Она не может покрыть всех случаев. И есть случаи, когда, мне лично, эти знания помогали в принятии одного из двух решений, не раз я видел подтверждение их в разного рода проектах. Тот же самый EShopOnContainers.

Только общие принципы, и пару примеров из опыта самого автора. Почему вы хотите увидеть какую-то конкретику? Если вас интересует какую лучше абстракцию над данными выбрать — так нужно именно это искать статьи, книги, спикинги умных людей. Книга, как по мне, скорее введение в правильное строение, что бы вообще понимать, что будет хорошо, а что плохо.

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

Вы себе стоимость такой имплементации для нетранзакционной распределенной системы представляете?


это поможет какое-то время НЕ принимать решения.

Ну так можно просто не принимать никаких решений. Что мешает?


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

Что такое "неправильная" архитектура, и как отличить ее от правильной? Гарантирует ли следование вашим "базовым принципам", что "неправильная" архитектура не получится?


И есть случаи, когда, мне лично, эти знания помогали в принятии одного из двух решений,

Как вы можете проверить, что принятые вами решения — правильные?


Почему вы хотите увидеть какую-то конкретику?

Потому что без конкретики это все бесполезно (и неоднократно описано).


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

Так что же будет хорошо-то? И что — плохо?


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

Да как обычно: собирать требования, формулировать их, выбирать решения.

Вы себе стоимость такой имплементации для нетранзакционной распределенной системы представляете?


Есть варианты, описаны в ещё одной книжке Designing Data-Intensive Applications.

Как вы можете проверить, что принятые вами решения — правильные?

Я лично — никак. Только общаясь с ребятами поопытнее и поумнее. Для этого и существуют тим-лиды в тиме и архитекторы — что бы подходить и советоваться, что лучше и как лучше сделать.

Что такое «неправильная» архитектура, и как отличить ее от правильной? Гарантирует ли следование вашим «базовым принципам», что «неправильная» архитектура не получится?

Ну, например, если мы уже говорили за книжку мелкомягких и их апликейшн — тогда поясню, что я думаю, было бы плохо. Точно было бы плохо написать Raw SQL запросы прям в контролер екшене, оттуда же отправить event на message-broker и залогировать все, к примеру. И это бы противоречило принципам, описанным в книге.
Есть варианты, описаны в ещё одной книжке

Да понятно, что есть, вопрос, во сколько они обойдутся.


Я лично — никак

Ну то есть вам лично эта книжка помогла принять решения, но вы не знаете, правильные ли они?


Ну, например, если мы уже говорили за книжку мелкомягких и их апликейшн

Я понятия не имею, что там за апликейшн.


Точно было бы плохо написать Raw SQL запросы прям в контролер екшене, оттуда же отправить event на message-broker и залогировать все, к примеру.

Почему?


Собственно, у меня в свежем проекте есть AWS Lambda, которая шлет прямой запрос в AWS CloudSearch, отправляет событие в другую лямбду, и все это логирует. Как раз как вы описали. Это плохая архитектура? Почему?


И это бы противоречило принципам, описанным в книге.

Я задал другой вопрос, если вы помните: "Гарантирует ли следование вашим «базовым принципам», что «неправильная» архитектура не получится?"


Впрочем, вопрос, какая же архитектура неправильная, почему, и как ее отличить от правильной, меня волнует больше.

Я задал другой вопрос, если вы помните: «Гарантирует ли следование вашим «базовым принципам», что «неправильная» архитектура не получится?»


нет не гарантирует — все индивидуально, я думаю. Мне кажется никакая книга ничего гарантировать не может, просто по причине того, что ее можно не правильно понять, как минимум.

Собственно, у меня в свежем проекте есть AWS Lambda, которая шлет прямой запрос в AWS CloudSearch, отправляет событие в другую лямбду, и все это логирует. Как раз как вы описали. Это плохая архитектура? Почему?

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

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

Ну то есть вам лично эта книжка помогла принять решения, но вы не знаете, правильные ли они?

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

Тогда какой в них смысл?


Я говорю не за лямбду, я с этим никогда не работал

Гм. А как тогда понять, куда можно применять принципы из книжки, а куда нельзя?


Как минимум это будет виолейтить SRP, если написать и логирование, и запросы в бд, и ивенты на меседж брокер в екшене контроллера.

И что? Почему это плохо?


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

Тааак… и что является аргументами для этого "думать"? Какие критерии? Как отличить правильное решение от неправильного?


я его неоднократно упоминал выше — семплик от майкрософтов

Вот я и говорю: понятия не имею.


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

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


Все зависит от сложности проблемы.

Ну так я говорю про конкретно те решения, которые вы приняли. Это были сложные проблемы или простые?

Тогда какой в них смысл?

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

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

Не прав в том, что задаю вопрос?


И на мой взгляд «следование базовым принципам» конечно не гарантирует что мы не получим «неправильную архитектуру», но уменьшает вероятность такого исхода.

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


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

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

Не прав в том, что задаю вопрос?

В том что уж совсем зашугали бедного strong junior developer'a :)

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

Конечно можно. Но точно так же её можно получить если иметь эксперта с многолетним опытом работы и т.д. и т.п. Вопрос в том какой шанс что получится плохая или хорошая архитектура в том или ином случае :)

И да, голову включать всегда надо и без этого всё равно не обойдёшься. И такие «базовые принципы» всего лишь немного «сокращают путь» и позволятют избежать пары-тройки лишних шишек(из бесчиcленного количества возможных :) ).
В том что уж совсем зашугали бедного strong junior developer

Это полезно. В том смысле, что задавать себе вопросы — очень полезно.


Вопрос в том какой шанс что получится плохая или хорошая архитектура в том или ином случае

И вот это очень интересный вопрос, да. И мой пойнт, not too subtle at that, состоит в том, что в статье описан случай, который с большей вероятностью приведет к плохой архитектуре, чем к хорошей.

Это полезно. В том смысле, что задавать себе вопросы — очень полезно.

Однозначно. И если честно я бы тоже сказал что молодой человек книжку прочитал, базовые принципы запомнил, но зачем они на самом деле нужны пока ещё не совсем понял. Но это как с огнём: надо один раз обжечься чтобы понять почему с огнём надо вести себя аккуратно.
И чтобы понять зачем нужны подобные принципы, SOLID, clean code, и куча других вещей надо на мой взгляд сначала на своём собственном опыте ощутить к чему приводит их отсутствие или неправильное использование.

И мой пойнт, not too subtle at that, состоит в том, что в статье описан случай, который с большей вероятностью приведет к плохой архитектуре, чем к хорошей.

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

Я честно скажу, я не помню, читал я ее, или нет. Мне нравится Мартиновский Clean Coder, но в первую очередь за не-технические советы. В остальном я разделяю высказанное тут неподалеку мнение: Мартин хорошо задает вопросы, но плохо дает на них ответы.

Ну она немного более «техническая» чем «clean coder», но всё ещё достаточно абстрактна и полна хороших вопросов. И я там тоже не всегда был согласен с конкрентыми «ответами». Но если уже читал другие книжки Мартина, то она вполне себе.

То есть если бы меня попросили её коротко охарактеризовать, то для эксперта она наверна уже бессмыслена и интересна только как беллетристика. Для «одинокого» джуниора немного опасна. А вот каким-нибудь миддлам и начинающим сениорам её вполне себе стоит прочитать.
П.П.С. Стоит ли «не экспертам» заниматься архитектурой это вопрос отдельный :)

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

Ну могу кинуть референс на СОЛИД и какие проблемы первая рекомендация может помочь решить, но думаю вы и сами найдете.
Тааак… и что является аргументами для этого «думать»? Какие критерии? Как отличить правильное решение от неправильного?
Как я бы то делал? Советовался, писал бы на форумы, думал сам бы — вот как.

Вот я и говорю: понятия не имею.
github.com/dotnet-architecture/eShopOnContainers

Ну так я говорю про конкретно те решения, которые вы приняли. Это были сложные проблемы или простые?

И такие бывали, и такие
Ну могу кинуть референс на СОЛИД

Ну то есть следование SOLID позволяет написать архитектуру, которая хороша, потому что она соответствует SOLID. Так?


Как я бы то делал? Советовался, писал бы на форумы, думал сам бы — вот как.

Если вас послушать, получается, что книжка про "чистую архитектуру" не дала вам ни одного (нециклического) критерия того, что же такое хорошая архитектура.


github.com/dotnet-architecture/eShopOnContainers

Я и говорю: понятия не имею.


И такие бывали, и такие

Давайте возьмем простые. Как вы можете проверить, что принятые вами решения — правильные?

Один пункт забыли: разумное следование.
ваше примечание.

Если вас послушать, получается, что книжка про «чистую архитектуру» не дала вам ни одного (нециклического) критерия того, что же такое хорошая архитектура.

Потому что архитектура — это не стандарт, я думаю прям абсолютной объективности здесь нельзя ожидать

Давайте возьмем простые. Как вы можете проверить, что принятые вами решения — правильные?


Окей, банальный свой проект, REST, решение: инапсулировать доступ к бд, и не писать в екшене контроллера RAW SQL, следуя, как минимум SRP & DIP. Спросил коллег: «стоит ли нагромождать екшн контроллера запросами к бд скюельными», получил ответ — «не стоит». Понял, что вероятность того, что не стоит, достаточно велика по сравнению с тем, что бы писать все в екшне.
Потому что архитектура — это не стандарт, я думаю прям абсолютной объективности здесь нельзя ожидать

А я не прошу объективности, я прошу нециклических критериев того, что такое "хорошая архитектура".


Окей, банальный свой проект, REST, решение: инапсулировать доступ к бд, и не писать в екшене контроллера RAW SQL, следуя, как минимум SRP & DIP.

Ну то есть ваш критерий правильности архитектуры — "коллеги согласились"?

А я не прошу объективности, я прошу нециклических критериев того, что такое «хорошая архитектура».

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

Ну то есть ваш критерий правильности архитектуры — «коллеги согласились»?


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

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


Пока да, это мнение других

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


(кстати, два абзаца в вашем комментарии противоречат друг другу)

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


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

Получается, что книжка для вас в этом оказалась бесполезна.
Почему же? Ведь перед тем как спросить, ты думаешь сам, читаешь, возвращаешься, опять таки, к принципам книги, и подбираешь какой-то вариант. Книга дает тебе набор принципов и «инструментов» с которых можно выбрать, не читая книги можно было о чем то не знать, либо знать о чем то не очень правильно и т.д.
Изменить, может и больше, но чтобы осознать не повредит это ещё чему-то, не поломает ли это логику чего-то ещё — проще все-таки лейерная архитектура

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


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

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


А в лейерной архитектуре — ты строго знаешь какой лейер за что отвечает

Откуда?


Почему же?

Потому что она не дала вам критерия, как правильно.

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


Я думаю зависит от сложности проекта. Если проект простенький, множества всяких либ и компонентов с сложной логикой там нет — тогда, почему нет?.. Но так или иначе, если например контролер в себе имеет логику написания чистого СКЛ к БД, это виолейшн SRP так точно. Не думаю что это хорошо.

Про сложность и независимость: Когда один екшн контролера занимает строк 400, то я думаю, для этого и придумали лейерную архитектуру(тот же самый DDD), что бы изолировать независимые между собою компоненты, что упрощает понимание, потому что конкретный компонент отправки меседжа на какой-то брокер легче понять, нежели искать куски этой логики где-то в 400 строках контроллера, может это только мне упрощает чтение и понимание логики. Если бы я увидел в каком-то сервисе, например, артиклах(представим что там ещё и серч енджайн юзается какой-нить), то если я бы увидел в контролере логику валидейшна инпута, запрос(и билдинг запроса) на серч енджайн, запрос(и билдинг запроса) на бд, логика склеивания это в оутпут, и отправку к примеру куда то ещё на другой сервис — то мне бы было сложно и неудобно с такой структурой. Противопоставлю этому пример: если бы я увидел 3 интерфейса — для серч енджайна, для бд, для отправки на внешний сервис, мне было бы легче и быстрее понять этот код, и что делает этот экшн в контроллере.

Если проект простенький, множества всяких либ и компонентов с сложной логикой там нет — тогда, почему нет?..

… то есть мы приходим к тому, что "хорошая архитектура" зависит от проекта? А как понять, какая архитектура хороша для конкретного проекта?


Но так или иначе, если например контролер в себе имеет логику написания чистого СКЛ к БД, это виолейшн SRP так точно. Не думаю что это хорошо.

Вы это уже говорили. Но почему это "не хорошо"?


Противопоставлю этому пример: если бы я увидел 3 интерфейса — для серч енджайна, для бд, для отправки на внешний сервис, мне было бы легче и быстрее понять этот код, и что делает этот экшн в контроллере.

Правда?


def q(a) =
  a | q | map (function
    | Success x -> x | s | option.map f
    | x -> x
  ) | map (function
    | Some x -> x | Content 
    | None -> HttpError.NotFound
    | Failure x -> x | HttpError
)
то есть мы приходим к тому, что «хорошая архитектура» зависит от проекта? А как понять, какая архитектура хороша для конкретного проекта?
Ну если простота сервиса позволяет не делать 100500 абстракций, лейеров и т.д. То нет нужды их делать просто так. Майкрософты в книжке хорошо про это писали. Один сервис они иплементнули просто как CRUD, за'DI'или ормку в контролер, и написали 4 метода CRUD. Потому что сервис простой, и его низкая сложность позволяет так сделать. С другой стороны — они применили DDD принципы при разработке другого -более сложного сервиса. Они могли их применить их и в случае первого, только смысла и денег для этого нет.
Ну если простота сервиса позволяет не делать 100500 абстракций, лейеров и т.д.

Она всегда позволяет.

Потому что она не дала вам критерия, как правильно.

Цитата автора с книги(architecture chapter): «Software was invented to be 'soft' It was intended to be a way to easily change the behavior of machines. If we'd wanted the behavior of machines to be hard to change, we would have called it hardware
To fulfill its purpose, the software must be soft — that is, it must be easy to change»

Ну то есть критерий "как правильно" — это легкость изменения? Все мои аргументы остаются валидными, значит.

Ну то есть критерий «как правильно» — это легкость изменения? Все мои аргументы остаются валидными, значит.


Напомните, какие аргументы? Я понмню только кучу вопросов.

Что для легкости изменения не нужна, скажем, архитектура со слоями (и много других "правил" тоже мешают).

Что для легкости изменения не нужна, скажем, архитектура со слоями (и много других «правил» тоже мешают).


Ну тогда зачем ребята делали абстракцию, например, на TCP\IP стеке?? Там тоже все на леерах, и она вроде-бы не течет, по крайней мере за столь бедный опыт, не было проблем с этим. Ребята дали абстракцию для работы с сетью (сокет), Никто не пишет логики поиска роута, логики транслирования битов в электрические или радио сигналы. ПОЧЕМУ? Почему сделали абстракцию над тем самым ассемблером, почему вы не пишите все на нем? Раз уж вы считаете, что абстракции текут — откажитесь совсем от них, они не несут, как вы сказали ничего полезного. По вашим словам, и простоты они не добавляют, и легкости изменения не дают.
Ну тогда зачем ребята делали абстракцию, например, на TCP\IP стеке?

За этим, я думаю, надо в устанавливающие документы идти. Но что-то мне кажется, что не для легкости изменений, нет.


ПОЧЕМУ?

Потому что так оказалось удобнее.


они не несут, как вы сказали ничего полезного

Неа, я этого не говорил.


Вот вам, кстати, еще один повод подумать (раз уж вы прошлый пример проигнорировали). Есть типичное такое asp.net WebAPI, смена пароля (хэшами не заморачиваемся), один POST с телом. В ответ от него прилетает "Password should be at least 6 letters long", надо это сообщение поменять на "Password should be at least 6 characters long". Только сообщение, больше ничего, всю логику надо оставить как есть.


Вот реализация WebAPI номер 1:


public void Post(string oldPassword, string newPassword)
{
  //...
  if (newPassword.Length < 6)
    throw new HttpResponseException(HttpStatusCode.BadRequest, "Password should be at least 6 letters long");
  //...
  user.Password = newPassword.
  user.Save();
}

Вот номер 2:


public void Post(PasswordChangeViewModel model)
{
  _userService.ChangePassword(model.OldPassword, model.NewPassword);
}

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

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

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

За этим, я думаю, надо в устанавливающие документы идти. Но что-то мне кажется, что не для легкости изменений, нет.
Не только за этим, но за этим в ТОМ ЧИСЛЕ. Потому что, с такисм пожзодом легко свичнуться например с UDP -> TCP, и при этом логика IP & LINK лееров будет не затронута, их не надо апдейтить, редеплоить и т.п. ВОТ одна из причин почему, я думаю. Плюс к тому, для борьбы со сложностью, потому что каждый леер представляет довольно сложно реализующуюся логику с разного рода алгоритмами и т.д.
Есть для этого разного рода валидаторы, от дефолтных аспнетовских — где можно атрибутами навешать почти все констрейнты — до флюент валидаторов.

Вот в этом и был мой вопрос: передо мной есть такой код, где мне искать это сообщение?


public void Post(PasswordChangeViewModel model)
{
  _userService.ChangePassword(model.OldPassword, model.NewPassword);
}

Но конкретно в этом примере — оставить это в контролере это нормально, потому что далеко не факт, что юзерСервис должен валидацией заниматься.

Гм. А я-то думал, проверка бизнес-ограничений в бизнес-слое быть обязана… И как же на самом деле надо делать?


Плюс к тому, для борьбы со сложностью

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

Гм. А я-то думал, проверка бизнес-ограничений в бизнес-слое быть обязана… И как же на самом деле надо делать?
Может быть я не прав, но это не бизнес правило — это валидация инпута.

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

Вы не правы. Длина пароля — это бизнес-правило.


(я, собственно, не понимаю, что за "валидация инпута", которая не вызвана бизнес-правилами)

Вы не правы. Длина пароля — это бизнес-правило.


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

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

(я, собственно, не понимаю, что за «валидация инпута», которая не вызвана бизнес-правилами)


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

Где-то, наверное, конвертируется. В приведенном примере (userService.ChangePassword(model.OldPassword, model.NewPassword)) модель, связанная с представлением, не покидает presentation layer.


никак не можно считать сходной моделью конроллера — бизнес моделью — опять таки виолейшн SRP.

Я не понимаю, что вы хотели сказать.


Даже в случае с юзер сервисом, должен быть валидатор, я думаю, как отдельный сервис, и в нем уже искать валидацию

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


либо же не усложнять себе жизнь и прописать все в контроллере. Если это позволяет сложность.

Что значит "позволяет сложность"? Весь пример — перед вами; сложность позволяет или нет?


Насколько мне известно, валидация инпута, и бизнесс-констрейнты — это разные вещи.

Это разные вещи, потому что одно — следствие другого. Это никак не отменяет того, что валидация инпута, не вызванная бизнес-правилами — редкая и специфическая вещь (и обычно ее все равно можно проследить до бизнес-требования).

никак не можно считать сходной моделью конроллера — бизнес моделью — опять таки виолейшн SRP.
Я не понимаю, что вы хотели сказать.


Входная модель контролера — не имеет ничего общего с Доменом

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


Если вы — новый человек в проекте — то нужно, наверное, спросить в ответственного человека — какая архитектура, и какой компонент ответственный за валидацию — и искать её там. Нету точного определения, где должна быть валидация, как и вообще нету конкретных стандартов правильной архитектуры, ну или киньте мне какой-то аналог RFC, где бы четко было прописано «как надо». Нужно прежде всего думать. А книга это набор принципов и инструментов, которые можно использовать — можно не использовать, никто же не запрещал. Только вот в нужном месте использование их — уменьшит вероятность нагавнить своим «велосипедом»
Входная модель контролера — не имеет ничего общего с Доменом

Не обязана иметь. Ну да, в примере так и есть. И что?


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

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


А книга это набор принципов и инструментов, которые можно использовать

А теперь давайте это сравним с вашим же утверждением из поста:


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

А теперь давайте это сравним с вашим же утверждением из поста:

И где тут противоречие? Следуя принципам, разумным, естевственно, вероятность налажать будет меньше, я думаю.
в том, что если скажут, там все по DDD, ты сразу поймешь где они должны бы быть.

И где же? Вот представьте себе, что я вам сказал, что в этом примере все по DDD. Дальше что?


Следуя принципам, разумным, естевственно, вероятность налажать будет меньше, я думаю.

Пока что вам не удалось никак эту мысль подтвердить.

И где же? Вот представьте себе, что я вам сказал, что в этом примере все по DDD. Дальше что?


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

Пока что вам не удалось никак эту мысль подтвердить.

как и вам — вашу. Как по мне, накидать все в екшн контроллера и сказать «что в этом плохого» — так себе идея. по крайней мере не видел такого ни в одном коммерческом проекте, и ни одного совета ни от одного дева, кроме вас, делать так.
Так или иначе, если вся валидация там или там,

Так как же узнать, в каком из двух (на самом деле — больше) "там" валидация, ни у кого не спрашивая? Вы только что говорили, что если сказано, что "по DDD", сразу понятно, где.


И не нужно будет искать каждый раз в разном месте.

Нужно, нужно. Даже у вас нужно как минимум два места обойти. А теперь я вам добавлю веселья: если этот же сценарий вызывается из браузера, проверка должна делаться на клиентской стороне (отзывчивость, вот это всё).


как и вам — вашу

Мне кажется, вы пока даже не понимаете мою мысль, о каком уж подтверждении тут говорить.

Нужно, нужно. Даже у вас нужно как минимум два места обойти. А теперь я вам добавлю веселья: если этот же сценарий вызывается из браузера, проверка должна делаться на клиентской стороне (отзывчивость, вот это всё).

Не нужно, если ты уже раз видел где валидация на проекте.

Так как же узнать, в каком из двух (на самом деле — больше) «там» валидация, ни у кого не спрашивая? Вы только что говорили, что если сказано, что «по DDD», сразу понятно, где.

Окей перефразирую — ты будешь знать где валидации точно не может быть. И я ещё раз повторяю, в вашем примере не вижу ничего плохого, что бы держать валидацию в контрллере. теперь представьте, там моделька на 15 пропертей, и в каждой делать иф, потом ещё логику в контроллер запихнуть — это уже посложнее будет выглядеть, и разобраться в этом посложнее будет. Ну лично мне проще перейди в какую-то папочку «validation», и разобраться там с 20 строками, нежели бегать по всему екшене на 400 строк.

Мне кажется, вы пока даже не понимаете мою мысль, о каком уж подтверждении тут говорить.

Ну пока, конкретные мысли которые вы пытались донести, кроме 100500 вопросов лично мне:
— абстракции текут, в прочем, разработчики ОС, например, разработчики TCP\IP доказали, что не всегда они текут.
— что на мое «напихать всю логику в контроллере» — вы ответили, «а что в этом плохого». И виолейшн обычного SOLID принципа — это в порядке вещей, и тоже нормально. С чем я тоже, не согласен. В большинстве случаев, это может привести к проблемам.
Не нужно, если ты уже раз видел где валидация на проекте.

Мы говорим о человеке, который первый раз пришел на проект, и еще не видел. Если уже видел, то понятно, что вопросов нет.


Окей перефразирую — ты будешь знать где валидации точно не может быть.

И где же?


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

Это автоматически означает, что валидация не в одном месте.


разработчик ОС, например, разработчики TCP\IP доказали, что не всегда они текут.

Нет, не доказали. Чего далеко ходить, вы про проблемы с HttpClient в .net не слышали?


что на мое «напихать всю логику в контроллере» — вы ответили, «а что в этом плохого».

Это тоже был вопрос, если что.


С чем я тоже, не согласен.

То, что вы не согласны, понятно. Почему не согласны — тоже понятно, в книжке написано, что это плохо. Это всё?


В большинстве случаев, это может привести к проблемам.

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

абстракции текут, в прочем, разработчики ОС, например, разработчики TCP\IP доказали, что не всегда они текут.

Кстати, забавно. Джоэл Спольски, который озвучил "закон текущих абстракций" ("All non-trivial abstractions, to some degree, are leaky"), показывает его на примере… TCP/IP.

Кстати, забавно. Джоэл Спольски, который озвучил «закон текущих абстракций» («All non-trivial abstractions, to some degree, are leak
Мельком пробежался по статейке, что-то в этом есть. Я не буду как то это комментировать, в следствие того, что человек имеет побольше опыта, я сейчас говорю о ТСП, не как о гарантии доставки, а о том, что они вынесли логику, независимую одну от другой отдельно. И это позволяет так просто свичнуться к примеру с TCP на UPD, и не нужно ничего делать с двумя нижними лейерами.
они вынесли логику, независимую одну от другой отдельно

Это никак не отменяет закона текущих абстракций. И конкретно абстрация TCP/IP — течет.

Окей, дайте точное определени «текущей» абстракции.

И что же вы скажете в этом случае про ОС. У них очень даже неплохо получилось абстрагироваться от железа. Насколько все проще становиться, когда тебя не заботит как все устроенно там, под коробкой, ты просто используешь интерфейс. Это экономит время, ресурсы.
Окей, дайте точное определени «текущей» абстракции.

Это написано в статье, на которую я дал ссылку, и по которой вы "пробежались".


This is what I call a leaky abstraction. TCP attempts to provide a complete abstraction of an underlying unreliable network, but sometimes, the network leaks through the abstraction and you feel the things that the abstraction can’t quite protect you from.

Проще говоря, текущая абстракция — это когда то, что за абстракцией все равно влияет на вас, как на пользователя.


И что же вы скажете в этом случае про ОС.

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


Да что там D-из-ACID, банальное различие между SSD и HDD может привести к тому, что один и тот же код выполняется быстро или медленно (и, наоборот, если мы хотим, чтобы код выполнялся быстро, нам надо прилагать дополнительные усилия). Абстракция? Да. Протекла? Да.

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


Ну окей, но от лейера IP & LINK абстрагороваться то получилось. И работает, ещё ни разу не слышал что бы один из них делал что-то некорректно. И их может юзать как TCP так и UDP, и их не заботит как он устроен.
Окей, но плюсы то все ровно есть

Плюсы — есть.


не нравиться SSD — юзни HDD, а вызывающий код трогать не надо.

… вот только он взял и просел по скорости на порядок, а так да, трогать ничего не надо.


Ну окей, но от лейера IP & LINK абстрагороваться то получилось.

Ключевое — как-то.

… вот только он взял и просел по скорости на порядок, а так да, трогать ничего не надо.
ну так поменять будет проще, из-за абстракции, ибо не тонешь в 100500 строках асма.

Ключевое — как-то.
Вы в этом участвовали? что точно знаете, что как-то? Так или иначе — получилось. Поэтому НЕ ВСЕ — текут. Я думаю можно сделать максимально не-текущие абстракции, вопрос в релевантности и денежных запасах.
ну так поменять будет проще, из-за абстракции, ибо не тонешь в 100500 строках асма.

Проще, чем в каком случае?


Вы в этом участвовали?

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


Поэтому НЕ ВСЕ — текут.

Цитата выше, спорьте с автором. Я не видел ни одной абстракции, которая бы не текла рано или поздно.

Проще, чем в каком случае?
чем если все запихнуть в один метод, в один компонент на 100000000 строк.
И при отладке сетей дома прыгать между уровнями приходится.
между какими уровнями?

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

А откуда вы взяли эту альтернативу? Я ее не озвучивал.


между какими уровнями?

Как минимум — application и transport. И я хорошо знаю, что нельзя просто взять и заменить tcp на udp — можно и связность потерять.


Пример IP лейер стэка — где здесь утечка?

В статье, которую вы проглядели.

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

В статье, которую вы проглядели.
там только за TCP. С этим, пожалуй пока соглашусь.

Как минимум — application и transport. И я хорошо знаю, что нельзя просто взять и заменить tcp на udp — можно и связность потерять.
ну да, апликейшн лейер будет зависеть от транспортного, потому что две разные имплементации транспортного в корне разные. Но вы глядите глубже, вам же не приходилось залезать в LINK & IP лейер?
но вы сказали «что плохого в том», когда я написал вариант засунуть в контролер логику серч енджайна, бд, логирования и конвертацию моделей.

Я спросил, что плохого.


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

Подождите, но как же, как же абстракция?


Но вы глядите глубже, вам же не приходилось залезать в LINK & IP лейер?

Лично мне не приходилось, я вообще сетями не занимаюсь.

Подождите, но как же, как же абстракция?
Простите, а где Я говорил что ВСЕ абстракции идеальные, и работают? Я говорю что НЕ ВСЕ плохие.
Я спросил, что плохого.
Если спросили — значит не знаете, или как?
Простите, а где Я говорил что ВСЕ абстракции идеальные, и работают? Я говорю что НЕ ВСЕ плохие.

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


Если спросили — значит не знаете, или как?

Значит, не знаю, почему вы утвержаете, что это плохо.

Значит, не знаю, почему вы утверждаете, что это плохо.
в таком случае я объяснил, почему, по моему, это плохо. Да и не только по моему.

Я говорил, что все — текут. Это не отменяет того, что даже текущие абстракции могут быть полезны, просто надо каждый (каждый!) раз помнить, что та абстракция, на которую ты сейчас встал, может потечь и потопить тебя.
Опять таки, пока что, не вижу утечки у IP & LINK лейера, и ОС. Даже в примере с SSD & HDD, разве ОС гарантирует скорость. Она гарантирует, что если система рабочая, она запишет и считает данные
в таком случае я объяснил, почему, по моему, это плохо.

Объяснить — объяснили. Но не убедили.


Она гарантирует, что если система рабочая, она запишет и считает данные

… и даже этого она не гарантирует.


А главное, какая мне разница, что она гарантирует? Мне важно, что я не могу просто сделать вид "это абстракция, что за ней — мне не важно", я должен помнить детали конкретных реализаций. Что хуже, иногда абстрация построена так, что я не могу выяснить, какая там реализация (что, в общем-то, еще хуже).

А главное, какая мне разница, что она гарантирует? Мне важно, что я не могу просто сделать вид «это абстракция, что за ней — мне не важно», я должен помнить детали конкретных реализаций. Что хуже, иногда абстрация построена так, что я не могу выяснить, какая там реализация (что, в общем-то, еще хуже).


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

… и даже этого она не гарантирует.
Ну почему же? Так, как и с TCP, есть условия, при которых есть гарантии. Так или иначе, имплементация не дает тоже никаких гарантий. Единственное, что дает гарантии в этом мире — это то, что мы все умрем. Если вы хотите гарантий — вам не к абстракциям.
Так а кто сказал, вообще не знать о деталях реализации?

Говорят, что information hiding — одна из задач абстракции. Нет?


Ну почему же?

По опыту.


Так, как и с TCP, есть условия, при которых есть гарантии.

Угу. Вот возьмем .net (полный, на винде):


FileStream fs;
//...
fs.Write(someBytes);

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

Говорят, что information hiding — одна из задач абстракции. Нет?
но кто сказал, что это идеально возможно во всех случаях. Тут скорее важно стремление к этому. есть много вообще полезных рекомендаций в жизни, но кто сказал, что их возможно постоянно и на 100% додерживаться?

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

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


Я точно сказать не могу, я же это не писал.

Подождите, один из смыслов абстракции ровно в том, чтобы вы могли ей пользоваться, хотя не вы ее писали. Или нет?


Я думаю, что при том, если все системы пк будут в норме.

Это ответ на вопрос "какие условия", но не на вопрос "когда".


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

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

Отменяет, в том-то и дело. Потому что вы поменяете то, что раньше люди узнали сквозь вашу абстракцию.


Простота и легкость изменения есть только тогда, когда у вас есть непроницаемый барьер (который и создает абстракция), который говорит, кому, что и как вы должны, и ничего больше. Все за пределами этого "должны" вы вольны менять. Если ваша абстракция протекла, и люди узнали больше, чем это "должны", вы уже не можете это так просто поменять.


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

Отменяет, в том-то и дело. Потому что вы поменяете то, что раньше люди узнали сквозь вашу абстракцию.
если действительно важно так знать, что-то об имплементации — об этих деталях можно оповестить людей. Если абстракции не делать, это никак не улучшит ситуацию. Все равно придется людей оповестить, что теперь то не юзайте, юзайте это.

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

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

Зато не надо тратить ресурсы на абстракцию, это уже хорошо.


Ну пока она не нужна — это работает.

А дальше вопрос отношения числа случаев.


Если становиться нужна, ну значит такая сложилась ситуация, подставить другую имплементацию под интерфейс

Вот только я не могу "подставить другую имплементацию под интерфейс", потому что я потребитель, мне все имплементации передают.


По поводу этого — можно пожалуйста пример, когда у вас такая ситуация возникала.

Как-то раз надо было нам запустить процесс парсинга asp.net-страницы внутри IIS. Подняли IIS-овский пайплайн, сделали фейковый запрос, запихнули — падает. Внутри в одном месте они не учли, что запросы бывают фейковые. Ну что, мы написали рядом костыль, котороый это место подменяет, чтобы работало правильно. А потом они в минорной версии поняли, что у них была ошибка, и починили ее — а наш костыль немедленно упал.

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

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

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

Нет, не может. Это одни и те же изменения.


кроме того, ты не там много потратишь на абстракцию

Вам это неизвестно.


В случае Артиклов 5 сек на создание интерфейса,

Вы за пять секунд проектируете интерфейс? Восхищаюсь вами. Я над одним названием метода дольше думаю.


не могу понять почему.

Потому что IoC и DI. Я, как потребитель сервиса, не определяю, какую реализацию сервиса мне передают, я должен работать с той, которую передали.


Простой пример, в DI в аспнете, когда сервис регистрируешщь, меняешь одну иплементацию на другую

Вы забываете про маленький нюанс: не все "сервисы регистрируете" вы. Что вам передали — с тем и работайте, инфраструктура вам не подконтрольна.


Баг в абстракции — не проблема абстракции, а проблема разработчиков — это не делает её — плохой.

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

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

Потому что IoC и DI. Я, как потребитель сервиса, не определяю, какую реализацию сервиса мне передают, я должен работать с той, которую передали
Но и без абстракции, вы будете потребителем, и будете работать с теми методами, которые вам представляют.

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

Баг не в абстракции, а в реализации. Я не понимаю, зачем просить пример, если любой пример можно отвергнуть с теми же аргументами.
То есть, если вы некорректно напишите mergesort — то проблема в алгоритме, а не в вас?

Нет, не может. Это одни и те же изменения.

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

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


Но и без абстракции, вы будете потребителем, и будете работать с теми методами, которые вам представляют.

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


а вот для людей, которые «передают», это упрощает жизнь.

Неа.


То есть, если вы некорректно напишите mergesort — то проблема в алгоритме, а не в вас?

То проблема в имплементации.


Я смотрю, вы любите некорректные аналогии.


Да, изменения те же, только чтобы поменять имя переменной в имплементации к примеру — вам не нужно будет ребилдить, и каким-то образом афектить «потребителя» абстракции — а при имплементации — придется.

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

Неа. Если имплементация используется «по месту», она не является долгосрочным контрактом, поэтому нет необходимости ее проектировать как публичную.
Я не за это. Ваша имплементация будет содержать ендпоинты, которые будут выполнять какую-то логику — так вот теже ендпоинты на интерфейс переложить, вот что я имел в виду — это не много времени. Так как в начале разработки, скорее всего, вы часто будете менять ендопоинты, но позже, когда разработка подойдет к финишу, будет просто стабильный интерфейс(стабильнее, чем имплементация)
Вот только я, например, смогу выбирать, какая реализация мне нужна.
ну если вы разработчик — можете, только вот если вы разработчик абстракции с имплементацией — то что мешает поменять имплементацию так же, как и в случае чистой имплементации?

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

Я смотрю, вы любите некорректные аналогии.
А чем плоха та аналогия?

Если имплементация сырая, то потребитель абстракции все равно будет поаффекчен.
Каким образом? Сразу пример. Имею либу(что бы сразу отпали вопросы, либа — потому, что логика будет использоваться повторно), и аспнет. В аспнете интерфейс, имплементация в либе. Есть один метод в интерфейсе, описанный. Билджу все — все окей. теперь, к примеру, нужно кусочек логики переписать в либе. Переписываю — билджу, аспнет не билдится. Почему? потому, что зависимости нету на либу. Либа зависит от абстракции. Все по Депенденси рулу. В случае вырой имплементации, аспнет бы тоже ребилдился.

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

Если это делать после разработки — то да, немного. Но тогда все время разработки я буду зависеть от имплементации.


ну если вы разработчик — можете, только вот если вы разработчик абстракции с имплементацией — то что мешает поменять имплементацию так же, как и в случае чистой имплементации?

Я не понимаю, что вы спрашиваете.


Да, ибо абстракция — упрощает, изолирует логики между собой, это упрощает задачу в изменении.

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


То, что майкрософты не углядели баг — не делает саму абстракцию плохой.

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


А чем плоха та аналогия?

Тем, что архитектура — это не дом.


Каким образом?

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


В случае вырой имплементации, аспнет бы тоже ребилдился.

Вы так этим восхищаетесь, как будто это что-то важное. Так вот — нет, не важное. А на билд-сервере все равно будет собираться все целиком.

Если это делать после разработки — то да, немного. Но тогда все время разработки я буду зависеть от имплементации.
начальное.

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

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

Вы просили пример ситуации, когда понадобилось залезть в детали реализации, и потом за это поплатиться? Вы их получили.
Вы привели пример БИТОЙ реализации. Приведите не битой, которая работает как-надо, а где проблему составила именно абстракция.

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

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

Неа. Все то время, пока контракт не стабилизировался.


Почему вы говорите об имплементации абстракции, как что-то от третьей стороны, а от просто имплементации, как о чем то своем?

Потому что весь смысл абстракции в том, чтобы разделить "ту" и "эту" стороны.


ну мне это не принесло дополнительных проблем, а только облегчало жизнь, на протяжении 1,5 года ентерпрайз разработки.

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


Приведите не битой, которая работает как-надо, а где проблему составила именно абстракция.

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


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

Ха! Ох уж эта мне сказка про "локальное как удаленное и удаленное как локальное", которую я со времен слышу никак не позже, чем remoting выкатили, а то и раньше. Нет, так не выйдет. Код для локального и удаленного взаимодействия надо изначально проектировать по-разному.


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

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

где вообще искать это сообщение во втором случае?

Кажется должно быть очевидным, что сообщение меняется либо там, где оно выводится (а не там, где кидается эксепшн), либо вообще в системе локализации.

У меня другой вопрос к этому примеру: как пользователь попал именно в этот файл? Раскручивая цепочку вызовов? Не проще ли было осуществить поиск по самому сообщению (по неизменяемой его части)?
Кажется должно быть очевидным, что сообщение меняется либо там, где оно выводится

А где это место?


У меня другой вопрос к этому примеру: как пользователь попал именно в этот файл?

Это точка входа.


Не проще ли было осуществить поиск по самому сообщению

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

Это точка входа.

Как он нашёл эту точку входа? Почему он искал именно её?

А где это место?

Вы мне скажите =) Если просто кинуть эксепшн и не обработать его, врят ли это сформирует корректный http-ответ клиенту (наверное, тут замешана какая-то абстракция? ;-))

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

Не совсем так) Если бы у нас была «архитектура», то мы бы знали, что все строки хранятся там-то и там-то (в системе локализации, в константах с описанием ошибок / сообщений / etc), а не просто лежат раскатанные по всему коду.

Как он нашёл эту точку входа?

Зная конвеер обработки запроса в asp.net WebAPI


Почему он искал именно её?

Потому что в задаче было сказано, какой запрос выдает ошибку.


Если просто кинуть эксепшн и не обработать его, врят ли это сформирует корректный http-ответ клиенту

Это зависит от конвеера, некоторые конвееры вполне себе это делают.


Если бы у нас была «архитектура», то мы бы знали, что все строки хранятся там-то и там-то

Ну так я и задал вопрос, где они хранятся, если архитектура соответствует тому, что описано в статье. Ответа пока не получил.

Ответа пока не получил.

Ответ я уже дал (в константах / системе локализации).
Или Вы рассчитываете на какой-то ещё?

Потому что в задаче было сказано, какой запрос выдает ошибку.

Так почему не искать саму ошибку? Или получается, что мы вновь и вновь будем возвращаться во все места, где происходит валидация пароля пользователя (например, при регистрации нового) и исправлять эту ошибку ещё раз и ещё?

Это зависит от конвеера, некоторые конвееры вполне себе это делают.

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

Ну то есть в системе, построенной по правилам из статьи, эта строчка всегда будет в файле с константами, или в под системе локализации? Так?


Так почему не искать саму ошибку?

Можно искать саму ошибку. Вопрос — где.


Ну т.е. мы всё равно должны знать этот конкретный нюанс, чтобы пользоваться данным кодом?

Конечно. Нельзя пользоваться asp.net WebAPI, не зная его конвеера.


Тогда чем он лучше другого варианта, если он тоже подразумевает под собой некую неявность?

Тем, что когда выбирается asp.net WebAPI, он уже навязывает один набор соглашений. Предлагаемый подход навязывает… второй?

Ну то есть в системе, построенной по правилам из статьи, эта строчка всегда будет в файле с константами, или в под системе локализации? Так?

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

Тем, что когда выбирается asp.net WebAPI, он уже навязывает один набор соглашений. Предлагаемый подход навязывает… второй?


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

А почему это "часто изменяемая вещь"? На мой взгляд, число таких символов меняется чаще, чем сообщение.


Возможно, набор тех или иных соглашений — и есть часть архитектуры?

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

А почему это «часто изменяемая вещь»? На мой взгляд, число таких символов меняется чаще, чем сообщение.

Ну, до тех пор, пока у вас это число хардкодится в сообщении, Вам придётся менять и его ;-)

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

Нет, я в первую очередь имею в виду число в условии этой проверки.


то в них периодически попадают ошибки, меняются формулировки, добавляются переводы.

"Периодически" — да. Чаще, чем в другом коде? Не обязательно.


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

Нет, я в первую очередь имею в виду число в условии этой проверки.

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

и архитектурные правила из статьи не дают однозначного ответа на вопрос, где искать такую ошибку.

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

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

Нет, я говорю, что число меняется чаще. Можно сделать его параметром для строки, и дальше конструкция этого всего станет еще интереснее.


на пункт сокрытие детали реализации.

… который обычно затрудняет поиск чего-то, а не наоборот.


но в целом это выглядит примерно так:
  • спросить у народа где хранятся строки

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

Удивительно, но в системе, которая разработана не по правилам выше, будет так же — спросить где хранится, и там найти. Получается, что в данном примере выигрыша нет.
так вы раз спросите, потом будете знать где искать ту или иную логику. Без абстрагирования, эта логика может быть везде, проще искать в каком-то участке(модуль, папка..) или во ВСЕМ проекте?
так вы раз спросите, потом будете знать где искать ту или иную логику.

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


Без абстрагирования, эта логика может быть везде

С абстрагированием — тем более везде. Я вас как-то просил привести список архитектурно правильных мест, где может быть это сообщение, вы до сих пор этого так и не сделали.


проще искать в каком-то участке(модуль, папка..) или во ВСЕМ проекте?

Полнотекстовому поиску все равно.

С абстрагированием — тем более везде. Я вас как-то просил привести список архитектурно правильных мест, где может быть это сообщение, вы до сих пор этого так и не сделали.
Я сделал это. Архитектура дает понять где что-то быть НЕ МОЖЕТ. А не где оно точно будет, ибо нету стандартов архитектуры, она может видоизменяться под нужды проекта.

Полнотекстовому поиску все равно.
Ну это пока проект не растет, и не усложняется
Архитектура дает понять где что-то быть НЕ МОЖЕТ.

И пользы с этого? Нам надо найти, где оно лежит, чтобы его поправить.


Ну это пока проект не растет, и не усложняется

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

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

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

Чем проще-то? Полнотекстовому поиску, как уже говорилось, все равно, а вбивать в него исключения — лишний труд.


При адекватной архитектуре число мест должно остаться прежним, или же меньшим в сравнении неиспользования архитектуры

При адекватной — да. Но верно ли это утверждение для архитектуры, построенной по правилам из вашего поста? Не уверен.

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

… но при этом:


И вот как строить эту архитектуру, как избавится от головной боли при маленьком изменении требований от PM’а, или от стейкхолдера: об этом и поведает книга
И если например бизнес логика навязывает один способ хранения данных как подходящий

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

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

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

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

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

И чем это отличается от "лучше не менять БД, и проектировать систему таким образом, чтобы менять БД не требовалось"?


Ну или скажите, при каком это будет дешево?

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


По объему выглядит как и в случае с абстракциями.

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

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

Так я же ровно про это и говорю: подход «давайте спрячем БД за абстракцией» создает иллюзию, что сменить БД будет дешево, хотя на самом деле — не будет.
Нет, в случае с абстракцией сменить БД будет дешевле, потому что у вас будет N абстрактных методов работы с данными и каждый метод будет использован в логике M раз. Итого, с абстракцией вам придется менять реализацию N методов, а без нее — N*M.

… только в случае с абстракциями мне, возможно, понадобится менять еще и абстракции. Так зачем мне этот дополнительный уровень геморроя?
Вряд ли. Если логика вашего приложения обращается к абстракции для сохранения или получения каких либо данных — это не изменится. Главное, чтобы особенности хранения не просачивались наружу.

Поймите, ваши данные не обязаны храниться в одном месте. Они вообще могут храниться и даже не в БД, а в стороннем сервисе. Вы можете использовать сущность Issue, которую будете брать/создавать в Redmine и ваш слой логики не обязан об этом знать. То же касается файлов. Хотите — храните в БД, хотите — на диске, хотите — в стороннем файловом хранилище. Вам будет намного проще управляться с этим, когда это будет в инфраструктуре, а не в логике.
Тем что выбранная вами БД может в итоге не покрыть всех требований к хранению данных и вам придется с этим жить.

Выбранная вами абстракция может в итоге не покрыть всех требований к хранению данных, и вам придется с этим жить.


Итого, с абстракцией вам придется менять реализацию N методов, а без нее — N*M.

Нет. Вы исходите из того, что, что удастся сменить БД без смены абстрации (сюрприз: нет, не удастся).


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

Они всегда просачиваются.


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


Вы можете использовать сущность Issue, которую будете брать/создавать в Redmine и ваш слой логики не обязан об этом знать. То же касается файлов. Хотите — храните в БД, хотите — на диске, хотите — в стороннем файловом хранилище.

Дадада, моему слою логики совершенно не обязательно знать, можно ли в один запрос найти все issues, которые используют вот такой файл в файловом хранилище, или надо перебрать все issue, и для каждого запросить файл.

Выбранная вами абстракция может в итоге не покрыть всех требований к хранению данных, и вам придется с этим жить.
Абстракцию я не выбираю, а проектирую. А вот СУБД мы выбираем. Это не просто игра слов — это огромная разница. Вы не можете вмешаться в работу СУБД, но можете вмешаться в абстракцию, если потребуется. А потребуется это в случае плохого проектирования или серьезного изменения требований. В большинстве случаев она будет просто расширяться.
Нет. Вы исходите из того, что, что удастся сменить БД без смены абстрации (сюрприз: нет, не удастся).
Удастся, если абстракция хорошо проработана. предположим, у вас есть сущность, поля которой не все фиксированные. Часть из них управляются администратором системы через конфиг.
Так вот, ваша доменная сущность должна выглядеть, так, как она бы выглядела, если бы сущность просто хранилась в памяти ну и чтобы в логике было удобно с ней работать. И абстрактный репозиторий будет работать с ней (именно с доменным типом). А ее модель БД уже может зависеть от конкретного выбора СУБД. если это PostgreSQL с его JsonB полями, то весь ваш набор динамических полей ляжет в одно единственное строковое поле и данные там будут в синтаксисе Json. Если же ваша СУБД такую динамику не поддерживает, вам, возможно, надо будет создавать отдельные таблицы для значений под каждый тип данных. То есть ваш репозиторий под капотом может содержать адаптер. Но какое дело слою логики до этого? А может вы под такую сущность решите использовать NoSQL. Опять же это будут детали реализации.
Я об этом и говорю: вы постулируете, что можно сделать идеальную неизменную абстракцию. Но на самом деле — нельзя.
Мы вообще живем не в идеальном мире. Если вы где то ошиблись при проектировании, у вас есть возможность расширить вашу абстракцию, добавив в нее новый метод. Старый будем считать устаревшим, пометим так, как этого требует ваш ЯП и в дальнейшем постепенно выпилим.
Они всегда просачиваются.
Это от недостатка фантазии, смекалки и переизбытка опыта проектирования через БД.
Однажды группе художников дали задание нарисовать каких либо невиданных монстров. Они нарисовали всякого, кто на что горазд. Но у всех монстров были в том или ином виде ноги/руки/лапы/крылья/хвосты и рты/пасти.
Дадада, моему слою логики совершенно не обязательно знать, можно ли в один запрос найти все issues, которые используют вот такой файл в файловом хранилище, или надо перебрать все issue, и для каждого запросить файл.
Конечно не обязательно. Ваш абстрактный репозиторий будет в слое логики, а вот его реализация в инфраструктуре. Так что да, ваш репозиторий будет либо принимать очень универсальные фильтры, либо иметь конкретный метод Issue[] GetIssuesThatContainsFile(file). А вот в реализации он уже может хоть через 1 запрос делать, хоть через пачку.
Абстракцию я не выбираю, а проектирую.

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


В большинстве случаев она будет просто расширяться.

Ну вот в этом у нас с вами и конфликт опыта. В моем опыте в большинстве случаев она не расширяется, а меняется.


Удастся, если абстракция хорошо проработана

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


Так вот, ваша доменная сущность должна выглядеть, так, как она бы выглядела, если бы сущность просто хранилась в памяти ну и чтобы в логике было удобно с ней работать.
[...]
Но какое дело слою логики до этого?
[...]
Опять же это будут детали реализации.
[...]
Ваш абстрактный репозиторий будет в слое логики, а вот его реализация в инфраструктуре. Так что да, ваш репозиторий будет либо принимать очень универсальные фильтры, либо иметь конкретный метод Issue[] GetIssuesThatContainsFile(file). А вот в реализации он уже может хоть через 1 запрос делать, хоть через пачку.

Been there, done that. Знаете, чем закончилось? Адскими проблемами с производительностью, потому что клиент ожидает, что будет работать как с памятью, т.е. можно просто взять и перебрать сущности, а реализация, внезапно, за каждой сущностью делает отдельный запрос, и это медленно. И нет, она не может делать общий, потому что хранилище не позволяет.

Абстракции для работы с хранилищами более-менее сформировались, и вы в первую очередь выбираете какую-то базовую, а потом допиливаете ее напильником.
Вот. Мы похоже об уровне абстракции не договорились. Если я вас правильно понял, вы имеете ввиду какой нибудь ORM. Но нет, я говорю про абстракцию уровня выше. Которая в реализации уже может работать с ORM, или с чем то еще в зависимости от хранилища.
… а хорошо проработана она тогда, когда у вас есть талант провидца. Потому что иначе может так получиться, что вы сделали приличную абстракцию на синхронных методах, а потом выяснили, что нужное вам хранилище умеет только асинхронные.
Ну вот в данном примере талант провидца не нужен. Все I/O операции лучше делать асинхронными. Сразу их под это затачивать на уровне абстракции. Если же в реализации они таки выполняют низкоуровневые операции или запросы синхронно, то стоит поискать другой инструмент. но если другого нет, пусть под капотом будут все те же синхронные запросы, но снаружи (на уровня потребителя вашей абстракции) выглядят асинхронными.
Знаете, чем закончилось? Адскими проблемами с производительностью, потому что клиент ожидает, что будет работать как с памятью, т.е. можно просто взять и перебрать сущности, а реализация, внезапно, за каждой сущностью делает отдельный запрос, и это медленно. И нет, она не может делать общий, потому что хранилище не позволяет.

Так вот видите, ваше хранилище не позволяет делать пакетный запрос, а вы на него положились и построили вокруг него приложение. Этого бы не произошло, если бы к выбору хранилища вы подошли чуть позже. Вы бы увидели, что ваша абстракция, которую вы спроектировали исходя из требований бизнеса, требует пакетные запросы, и выбрали бы хранилище, которое это умеет.
Если я вас правильно понял, вы имеете ввиду какой нибудь ORM. Но нет, я говорю про абстракцию уровня выше.

Нет, вы меня неправильно поняли. Я имел в виду как раз "абстрацию уровня выше", там где какие-нибудь репозитории, спецификации, и вот это вот все.


Все I/O операции лучше делать асинхронными. Сразу их под это затачивать на уровне абстракции.

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


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

Ну то есть выбранная ранее абстракция, внезапно, все-таки ограничила нам выбор хранилища. Это ровно то, о чем я говорил.


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

Нет, вы меня неправильно поняли. Я имел в виду как раз «абстрацию уровня выше», там где какие-нибудь репозитории, спецификации, и вот это вот все.
Так, и где мне взять готовенькую абстракцию или реализацию под мою бизнес задачу? Если вы и берете чьи то наработки, то это должны быть ничем не ограничивающие вас плюшки, которые помогут написать меньше кода. Остальное — за вами.

Это если у вас (а) есть абстракция под асинхронию
Если вы про async/await в дотнете, то там и до этого была асинхрония, только выглядела несколько по-уродски. Я и сам ей не пользовался из-за лени и, на тот момент, недостатка понимания кое чего про потоки.

(б) эта абстракция не несет существенных накладных расходов (а это то, что мы сейчас мучительно меряем).
Так, постойте, абстракция призвана упростить жизнь, а не усложнить ее. Накладные расходы — вина реализации. Да, не вашей, но если все очень плохо, скорее всего это поправят в ближайших версиях.
Был у меня случай. Показывают код, говорят что тормозит жутко. Программист пытался заниматься эквилибристикой с async/await чтобы снизить накладные расходы, но не хватало знаний, поэтому обратился. Что я сделал? Изменил сложность алгоритма с O(n) на O(1). Когда накладные расходы перестали стрелять n раз, жить стало веселее. Так же неоднократно видел, как люди пытаются ускорить SQL запросы, а дальше идет код, который n раз обходит результат из m записей, когда в этом нет необходимости.

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

При этом нет, это не «требования бизнеса». Это требования клиентского кода, который привык работать, как с памятью. А у бизнеса таких требований не было. Если бы мы заранее знали, что хранилище так не может, мы бы построили абстракцию иначе, не позволяя клиенту такие запросы.
Я не понял, к сожалению, о чем тут речь. Я не знаю, что такое требование клиентского кода. Но мне не очень нравится, что вы готовы ограничить пользователя в ништяках, потому что ваша СУБД чего то там не умеет. Видимо, пути у нас с вами разные.
Так, и где мне взять готовенькую абстракцию или реализацию под мою бизнес задачу?

Например, PoEAA. Или Эванс.


Если вы про async/await в дотнете, то там и до этого была асинхрония, только выглядела несколько по-уродски.

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


Накладные расходы — вина реализации.

Не обязательно. Неправильно спроектированная абстракция тоже может их приносить.


Да, не вашей, но если все очень плохо, скорее всего это поправят в ближайших версиях.

Вот только мой кастомер не будет этого ждать, понимаете?


Так это же наоборот хорошо!

Если это хорошо, то решение "мы работаем только с такой БД" в начале проекта — это тоже хорошо.


Я не знаю, что такое требование клиентского кода.

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


Но мне не очень нравится, что вы хотите ограничить пользователя в ништяках, потому что ваша СУБД чего то там не умеет.

Ну а как иначе-то? Если нижележащий слой не умеет, и заменить его нельзя, то пользователь будет ограничен, это весьма неизбежно.

Например, PoEAA
Так это шаблоны. Ясное дело, в ваших абстракциях будут угадываться эти паттерны, по одному или набором. Но приложение же из одних шаблонов не склеишь. Кастомизация в данном случае то, за что нам и платят такие деньги, а не склеивают приложение в каком-нибудь скрэтче :)
И та асинхрония, которая там была до этого, была настолько уродской, что я не видел ни одного DAL, который ее поддерживал как первоочередную функциональность
Вы и сейчас без нее можете обойтись, она не является каким то обязательством.Но если есть какой нибудь шанс, что ваше приложение будет работать на слабой технике, что будет медленный HDD, что будет медленная сеть с постоянными провалами, или что возможны кейсы, когда будет читаться/писаться большой объем данных куда либо, лучше не пренебрегать асинхронностью.
Не обязательно. Неправильно спроектированная абстракция тоже может их приносить.
Нут ту я, все же, не стал бы это учитывать. Абстракция, которая влечет большие накладные расходы — это надо постараться. А если таки это получилось, то не стоит относиться к этому как к полному провалу. Это бесценный опыт, который позволит не допустить подобного в следующий раз.
Вот только мой кастомер не будет этого ждать, понимаете?
Понимаю, в этом случае использование проблемной части фреймворка можно обернуть еще в одну абстракцию (если требуется), накостылить временный обход проблемы, а затем, когда все исправится, сделать по красоте.
То есть там, где вы бы написали MyFramework.DoAny(), вы напишите просто DoAny() или _myAbstraction.DoAny(), если повторяется в разных частях кода, а там уже написать собственную реализацию. Временно. А может и постоянно. Главное, чтобы она вас устраивала. Ну и ее можно переписать всегда. А когда разрабы фреймворка все исправят, вы свою реализацию можете просто заменить на MyFramework.DoAny(). Не трогать абстракцию, а просто подменит ее реализацию.
Тут меня могу обвинить в оверхеде, но на самом деле я бы рекомендовал все такие штуки оборачивать в собственную абстракцию. Математические функции из класса Math, работу с датой, типа DateTime.Now и многое другое. Просто потому, что в один прекрасный момент вам надо будет синхронизироваться с сервером в другой таймзоне, при этом системное время останется актуальным, а возвращать надо будет другое. И вы обнаружите, что вам придется менять это в 2000 местах. И тут просто автозамена не прокатит, потому что надо будет юзинги прописать.
Если это хорошо, то решение «мы работаем только с такой БД» в начале проекта — это тоже хорошо.
Почему вы упорно не хотите принять, что СУБД — это инструмент? Ведь все что я говорю, основывается именно на этом. Для разных целей вы выбираете подходящие для этого инструменты. А вы говорите, что у меня есть только молоток и гвозди, поэтому дом я вам построю деревянный.
Это когда код написан таким образом, что у него есть ожидания от вызываемого кода. Например, он ожидает. что стоимость операции пренебрежима, и поэтому вызывает ее часто.
Я перефразирую, чтобы убедиться, что я все правильно понял. Вы создали некий функционал по ТЗ. Среди прочего была операция, которая выполняется долго, но пользователи хотят выполнять ее очень быстро (спамят на кнопочку, например). И при этом в ТЗ не было про это сказано. Так?

Ну а как иначе-то? Если нижележащий слой не умеет, и заменить его нельзя, то пользователь будет ограничен, это весьма неизбежно.
Так вот. Смотрите, если бы была абстракция над хранилищем, то вы бы сказали заказчику: «Чтобы исполнить ваши хотелки, нам придется переписать 5-15% кода (замена реализации абстракции для использования другой СУБД)». Заказчик бы подумал, стоит ли этот ништяк своих денег и, вероятнее всего, решит что стоит. Может не сразу, а после постоянного нытья пользователей. А в вашем случае переписывать придется 60-80% кода, И по времени это будет не пропорционально тем 5-15%, это будет в разы сложнее.
Так это шаблоны.

Ну вот из шаблонов вы и выбираете обычно.


лучше не пренебрегать асинхронностью.

Ну вот видите. Сейчас — "лучше не пренебрегать". А лет пять-восемь, или когда мы там свой интерфейс проектировали, была ситуация "лучше не трогать без критической надобности". А вы говорите — проектировать интерфейс без умения предсказывать будущее.


Нут ту я, все же, не стал бы это учитывать.

… давайте учитывать только достоинства, и не учитывать их недостатки? Удобно, что уж.


Понимаю, в этом случае использование проблемной части фреймворка можно обернуть еще в одну абстракцию

Нельзя. От того, что вы обернули проблемную часть, быстрее не станет.


Тут меня могу обвинить в оверхеде, но на самом деле я бы рекомендовал все такие штуки оборачивать в собственную абстракцию. Математические функции из класса Math, работу с датой, типа DateTime.Now и многое другое.

Угу. Написать свои абстракции на все подряд. А потом радоваться, как оно интегрируется с чужим кодом.


Почему вы упорно не хотите принять, что СУБД — это инструмент?

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


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

Было об этом сказано в ТЗ.


Смотрите, если бы была абстракция над хранилищем, то вы бы сказали заказчику: «Чтобы исполнить ваши хотелки, нам придется переписать 5-15% кода (замена реализации абстракции для использования другой СУБД)». Заказчик бы подумал, стоит ли этот ништяк своих денег и, вероятнее всего, решит что стоит.

Внимание, сюрприз. У заказчика эта СУБД лежит в основе инфраструктуры и забюджетирована на пять лет вперед. Поэтому менять он ее не будет.


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


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

Прошу прощения за некропост, но уж очень интересная тема. Я так понимаю ваш тезис такой: может выйти такая ситуация, когда заказчик требует работать с его БД, при этом у вас уже существует продукт со своими абстракциями, которые вы переносите на условия клиента. Ну или пишите сразу там, но как-то упустив проблему производительности из виду. И в этой ситуации без абстракции будет лучше т.к. все равно надо будет переделывать работу с БД, а тут еще и абстрацию править.

Все верно?

Не, мой тезис такой: может выйти такая ситуация, когда разработанная "заранее" абстракция только вредит последующей разработке.

Может. Но вопрос в том, как оно «в среднем». Думаю, в том, что в среднем наличие абстракции скорее упрощает изменение, чем отстутствие таковой абстрации.

Ну вот в моем опыте наблюдений - нет. Разработанные "на будущее" абстрации чаще неудачные, чем удачные.

Это все к тому, что красивые фразы «это все детали реализации» обычно так и остаются красивыми фразами, потому что суровая реальность намного сложнее, чем простые примеры.

Это конечно все так. Но не потому, что абстракция плохая и не работает. Чаще всего просто не хотят / не умеют четко разделять слои, считают что это слишком долго, дорого и т.д.

Тот же переход с реляционки на KV например. Если сразу правильно спроектировать слой доступа к данным, то там будет, например, для сущности Customer интерфейс с необходимыми для работы приложения методами.
interface CustomerRepository {
  EntityCustomer getById(Long id);
  EntityCustomer getByName(String name);
  EntityCustomer getByManyManyAttrs(many many attrs ......);
  EntityCustomer save(EntityCustomer entity);
...............
}

Поменять реализацию этого интерфейса — поменяется хранилище. На что угодно. В том числе и на KV.
Да, там будет много проблем с поиском не по ключу, с объединением данных и т.д. и т.п. Это может действительно оказаться очень долго и дорого. Но! Это реализация только слоя хранения. Те самые «детали реализации». Все остальное уже разработано, протестировано. И трогать не надо ни бизнес логику, ни UI, ни что-то другое.

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

Совершенно другой вопрос — откуда необходимость столь радикальной смены хранилища? Слишком суровый архитектурный косяк, imho.
Это конечно все так. Но не потому, что абстракция плохая и не работает.

Конечно. Это потому, что абстракции текут.


Если сразу правильно спроектировать слой доступа к данным

Как отличить правильно спроектированный слой доступа к данным от неправильно спроектированного?


поменяется хранилище. На что угодно. В том числе и на KV. [...] Все остальное уже разработано, протестировано.

Угу. Возьмем один метод, getById(id), и посмотрим на его свойства. getById(1) == getById(1)? А вот так: getById(1).name = 'def'; print(getById(1).name)? А вот так: getCustomerRepository().getById(1) == getCustomerRepository().getById(1)? А то же самое между потоками? А между процессами (передача сериализованных данных)?


Теперь добавим еще один метод: save(entity).


//thread 1
r1 = getCustomerRepository()
c1 = r1.get(1)

//thread 2
r2 = getCustomerRepository()
c2 = r2.get(1)

//thread 1
c1.name = 'def'

//thread 2
c2.email = 'a@b.c'
r2.save(c2)

Что сохранится?


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


Все остальное уже разработано, протестировано. И трогать не надо ни бизнес логику, ни UI, ни что-то другое. [...] После такого перехода конечно же поменяются характеристики работы приложения

Подождите. Вы же только что говорили, что все уже разработано и протестировано, а теперь говорите, что характеристики поменяются. Если поменялись характеристики — надо перетестировать, а если они стали неудовлетворительными — переписывать. И нет, переписывать только DAL окажется недостаточно. А это значит, ваша абстракция снова протекла.


Совершенно другой вопрос — откуда необходимость столь радикальной смены хранилища?

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


Слишком суровый архитектурный косяк

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

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


хочешь мира — готовься к войне

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

Стоимость этой подготовки посчитать не забыли?


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

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


Ну и да, это не отменяет исходного вопроса: если вам пришлось поменять хранилище — это архитектурный косяк, или нет?

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

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

Я не понимаю, что вы хотите сказать.


Опять же вы хотите получить конкретный ответ на абстрактный вопрос.

Ну да, потому что из таких вопросов и состоит выбор архитектуры. Странно, да?

Ну да, потому что из таких вопросов и состоит выбор архитектуры. Странно, да?
так давайте конкретный пример и обсуждать, а не абстрактно говорить, если хотите не абстрактных и цикличных ответов.

ну а если внесет — ресурсы будут больше, насколько мне известно.

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

Ну вот вам конкретный пример: я пишу веб-сервис (в том смысле, что он общается с внешним миром посредством JSON по HTTP), который должен обеспечивать полнотекстовый поиск по базе статей и отслеживать качество этого поиска на основании пользовательского отклика. Как мне оценить качество его архитектуры?


Если захочет внести эти изменения потом — они выйдут дороже

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


Ошибки проектирование — самые дорогие

Вот только это не ошибка проектирования.

Ну вот вам конкретный пример: я пишу веб-сервис (в том смысле, что он общается с внешним миром посредством JSON по HTTP), который должен обеспечивать полнотекстовый поиск по базе статей и отслеживать качество этого поиска на основании пользовательского отклика. Как мне оценить качество его архитектуры?


Я думаю, что на протяжении развития проекта, будут возникать новые требования на новые фичи, если текущая архитектура будет затруднять по ресурсам и времени их имплементацию. Во время обдумывания имплементации новых фич, вы осознаете, к примеру, что если бы я когда-то сделал по -другому, не было бы такой проблемы сейчас. И вот чем больше таких проблем — тем хуже архитектура, я думаю. А сразу оценку сделать, сложно я думаю. На то и придумали такие рекомендации как SOLID и Dependency Rule, что бы четко видеть, где может быть плохо сразу. По этому если ребята берут DDD, то держат домен независимым, по тому же Dependency rule. Хотя в в тот момент это может быть и не нужно, но для возможных будущих проблем это делается.
И вот чем больше таких проблем — тем хуже архитектура, я думаю.

Вот только это будущее, а мне сейчас писать надо. Как мне сейчас писать?


Я еще раз напомню вашу фразу из статьи:


И вот как строить эту архитектуру, как избавится от головной боли при маленьком изменении требований от PM’а, или от стейкхолдера: об этом и поведает книга

Так как же мне "строить эту архитектуру"?

Вот только это будущее, а мне сейчас писать надо. Как мне сейчас писать?

Есть готовые решения, которые минимизируют возможность налажать, готовые принципы — которым можно следовать. В книге эти принципы и описаны.

Окей, возвращаемся на шаг раньше. Какие готовые решения и принципы надо применить при построении моего сервиса, и почему?

Ну как минимум SOLID, Reuse\release equivalenxe, common closure, common reuse principles, и ещё ряд рекомендаций, почему — там автор и говорит.

"Там автор и говорит" без цитаты не отвечает на мой вопрос.


Например, расскажите мне, как именно я должен применить REP к своей задаче. Или, еще веселее, CCP — что именно является компонентом в моей задаче?

Например, расскажите мне, как именно я должен применить REP к своей задаче. Или, еще веселее, CCP — что именно является компонентом в моей задаче?

окей, по CCP: если нужно полнотекстовый поиск — это отдельный кусок логики, который можно выделить как отдельный компонент:
Gather into components those classes that change for the same reasons and at the same times.
Потому что что запихнуть туда ещё и логику валидации — это виолейшн этого принципа — ибо валидация может менятся независимо от серч енджайна. Вот, один простенький пример

Гм. У меня весь код, отвечающий за полнотекстовый поиск — это формирование HTTP-запроса к ElasticSearch. Вы мне предлагаете его выделять в отдельный компонент?

Гм. У меня весь код, отвечающий за полнотекстовый поиск — это формирование HTTP-запроса к ElasticSearch. Вы мне предлагаете его выделять в отдельный компонент?
Да, потому что, допустим, потом появится реквайрмент считать количество запросов от юзера, илислать какой-то ивент на екстернал, когда серч енджайн тыкнули — это может призвести к виолейшн перечесленных принципов. Поэтому, я б вынес.

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

Ладно, давайте я попроще вопрос задам, чтобы на примере было. У меня есть две архитектуры-кандидата, между которыми я выбираю:


  1. сервис на базе asp.net WebAPI, который принимает запросы от пользователей, отправляет их в ElasticSearch, отдает обратно отформатированными, принимает фидбек, пишет его в хранилище.
  2. решение на базе AWS, где API Gateway принимает запрос, перекидывает его в Lambda, та прокидывает в эластик, а потом результаты обратно, фидбек идет напрямую через API Gateway в хранилище.

Какую архитектуру из двух мне выбрать и почему?

Я не знаю, как работает aws lamda. Поэтому я не знаю. Я бы выбрал первый, если это единственные реквайрменты.
Было бы два микросервиса, один на артиклы, другой на фидбек. Если нужно будет что-то дополнительное реализовать, мне будет проще это добавить. К примеру скажу что может поменятся. Артиклы могут расти, и хранить все в еластике станет лишним. Например, в моем проекте, все с серч енджайна тянется только айдиха, потом уже от модельки, мы подтягиваем необходимые данный с постгресса. Это к слову тоже отдельные логики, ибо постгрес может с легкостью сменится на MYSQL, к примеру. И что бы в одном методе не менять какой то синтаксис SQL, и редеплоить все — я поменяю это только в компоненте, отвечающего за доступ к бд.
Было бы два микросервиса, один на артиклы, другой на фидбек.

Почему два? Вы понимаете недостатки двух микросервисов?

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

Почему два? потому что принципиально разные ответственности и причины для изменения у них.
Это даст сложности в деплоинге, в разроботке, по финансам

Забыли латентность, а это для пользователя важнее всего.


если говорить, о том случае, когда проект начнет расти — это окупится

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


Почему два? потому что принципиально разные ответственности и причины для изменения у них.

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


(забавно при этом, что выше вы предлагали другую декомпозицию, но уже про нее забыли)

Забыли латентность, а это для пользователя важнее всего.


А как это повлияет на латентность?

Не «когда», а «если». Если вы доживете до момента, когда проект начнет расти, а не погрязнете в разработке согласованного деплоя, системы коммуникации, протокола обмена с клиентом и прочих мелочей, которые вас тормозят.


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

(забавно при этом, что выше вы предлагали другую декомпозицию, но уже про нее забыли)

имелась в виду эта, может я не правильно выразился
А как это повлияет на латентность?

Вообще-то, она увеличится, потому что вместо обращения к одному компоненту будет обращение к двум, а пересечение границы компонента — это дорого.


Естественно, для этого архитекторы собираются с ПМами и т.д., что бы оценить риски, и стоит ли делать вот так и все сразу, или что-то пока что оставить на потом.

… и если они оценят иначе, то ваша архитектура перестанет быть правильной, и станет правильной какая-то другая?


Мне, конечно, нравится, как вы элегантно проигнорировали нарушение CCP в вашей декомпозиции (а при этом продолжаете говорить, что нарушение SRP в классе — это плохо).

Вообще-то, она увеличится, потому что вместо обращения к одному компоненту будет обращение к двум, а пересечение границы компонента — это дорого.
в контексте задачи — это все равно два различных запроса. Какая разница обращаться на два сервака, или на один? Или вы хотели впихнуть в один запрос и сохранение фидбека и доставание артиклов??
… и если они оценят иначе, то ваша архитектура перестанет быть правильной, и станет правильной какая-то другая?

нет, она станет не релевантная в данном случае.
Мне, конечно, нравится, как вы элегантно проигнорировали нарушение CCP в вашей декомпозиции (а при этом продолжаете говорить, что нарушение SRP в классе — это плохо).
вполне может быть — я не архитектор, и когда привел пример, я не советовался вообще ни с кем, а с потолка взял то, что первое в голову пришло. Где же там нарушение?
в контексте задачи — это все равно два различных запроса.

Неа, первый фидбек пишется в момент прохождения запроса в полнотекстовый поиск (чтобы сам факт запроса фиксировать).


нет, она станет не релевантная в данном случае.

Вот видите. Вы все сделали по вашим правилам, а архитектура все равно "не релевантная".


Где же там нарушение?

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

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

Вот видите. Вы все сделали по вашим правилам, а архитектура все равно «не релевантная».


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

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

Нет, это фидбек о работе поиска, в контексте конкретных результатов. Так что, будете архитектуру менять?


Но такого разговора не было, никто ничего не решил — поэтому ещё не не релевантная.

… но до такого разговора нельзя принять решение о выборе архитектуры.


И какая там причина изменения кроме причины изменения связанная с серч енджайном?

У сервиса полнотекстового поиска:


  • смена контракта поискового движка
  • смена клиентского контракта
  • смена поведения
  • смена контракта сервиса фидбека

У сервиса фидбека:


  • смена контракта хранилища
  • смена клиентского контракта
  • смена бизнес-логики

(я, заметим, еще не факт, что все вспомнил)

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

public async Task<List<Article>> GetArticlesAsync(string searchWord){
var articles = _engineAbs.GetArticlesBySearchWord(searchWord);
_externalInteractor.SendFeedback();
}


Теперь изменения в посыле фидбека никак не влияет на гет статей.
вот эта ответственность и всплывает, когда набросать все в один метод.

А какая разница, в скольких оно методах, мы про компоненты говорим.


Если посыл фидбека вынести как отдельный клас с интерфейсом — эта ответственность пропадает.

Никуда она не пропадает, она все так же в том же компоненте. В другом классе, но в том же компоненте.


Я вас еще раз спрошу: вы вообще помните определение компонента из книги, на которую вы ссылаетесь?

Никуда она не пропадает, она все так же в том же компоненте. В другом классе, но в том же компоненте.

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

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

Теперь у вас появился контракт между этим компонентом и вашим. Компонентов стало больше, контрактов стало больше, но проблема никуда не исчезла.


потому что ее можно отдельно задеплоить.

Но вообще, конечно, нельзя. Деплоить надо релиз целиком, потому что REP.


Даже на уровне классов и методов принципы остаются рабочими.

На уровне классов и компонентов этот принцип называется SRP, и там ровно те же проблемы, просто с другими словами.

Теперь у вас появился контракт между этим компонентом и вашим. Компонентов стало больше, контрактов стало больше, но проблема никуда не исчезла.

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

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

На уровне классов и компонентов этот принцип называется SRP, и там ровно те же проблемы, просто с другими словами.
для того придумали Dependency Inversion.
но через инвертирование зависимостей все стает независимым, независимо деплоябельным, и независимо девелопабильным.

Нет, не станет.


Чтобы у вас появилась независимая разработка, вам надо разнести это по разным проектам, положить каждый в независимый версионник и настроить там сборку (желательно вместе с CI, чего уж). И это не забыв про то, что вам надо как-то прокинуть общеиспользуемый интерфейс между этими двумя проектами.


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


А теперь вопрос — зачем мне это? Зачем мне "независимая разработка" для функциональности размером в десять строк кода? Зачем мне независимое развертывание для куска кода, который служит тонкой прослойкой между остальным кодом и ElasticSearch? Что я такого получу, что объяснит моему заказчику, почему я потратил на это в два с половиной раза больше ресурсов?


И знаете, что самое грустное? Что CCP все равно нарушен.


Почему?

Потому что нормальная единица развертывания для asp.net-сервиса — это сервис целиком, а не отдельные сборки внутри него. Если вы хотите подменять сборки по отдельности, вам придется сколхозить собственный набор механизмов доставки и обновления.


если н е устраивает фидбек, или его нужно слать ещё в одно место, к примеру, что мешает написать эту логику, и задеплоить только этот микросервис?

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


для того придумали Dependency Inversion.

Для чего именно "того"?

А теперь вопрос — зачем мне это? Зачем мне «независимая разработка» для функциональности размером в десять строк кода?
ну тут мы говорим о рентабельности того или инного подхода, а не о том, что он плохой.

Касательно зависимости фидбека и артаклов. Окей, вы предлагаете иметь все в одной сборке. Теперь предположим, у вас появился сервис юзеров, там тоже нужно делать фидбек(слать меседж в какой-то екстернал сервис). Эта логика уже есть в артиклах, вы предлагаете это копипастить, или же юзеры теперь будут зависим компонентом от артиклов?

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

Потому что нормальная единица развертывания для asp.net-сервиса — это сервис целиком, а не отдельные сборки внутри него. Если вы хотите подменять сборки по отдельности, вам придется сколхозить собственный набор механизмов доставки и обновления.
Можно это сделать через РЕСТ, объявить фидбек, как интернальный микросервис, и все делать через него, и мы сможем независимо деплоить его.
ну тут мы говорим о рентабельности того или инного подхода, а не о том, что он плохой.

А разве можно считать хорошим нерентабельный подход?


Касательно зависимости фидбека и артаклов.

Вы опять переводите тему. Нет, не касательно "зависимости фидбека и артаклов". Меня интересует только выделенный вами микросервис поиска, который — в вашей декомпозиции! — нарушает CCP. Про фидбек поговорим потом.


Окей, вы предлагаете иметь все в одной сборке.

Нет, не предлагаю.


для того, чтобы убрать явные зависимости

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


Можно это сделать через РЕСТ, объявить фидбек, как интернальный микросервис

Я еще раз повторю, мне не сложно: мы. говорим. не. про. фидбек. Мы говорим про один микросервис поиска по статьям.

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

Вы опять переводите тему. Нет, не касательно «зависимости фидбека и артиклов». Меня интересует только выделенный вами микросервис поиска, который — в вашей декомпозиции! — нарушает CCP. Про фидбек поговорим потом.

Окей уберем фидбек, есть сервис, который отвечает только за артиклы(полнотекстовый поиск), где здесь виолейшн? Пришел запрос на вебсервис, пошел на абстракцию серч енджайна — вернул данные, где тут виолейшн ССP?

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

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

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


Окей уберем фидбек, есть сервис, который отвечает только за артиклы(полнотекстовый поиск), где здесь виолейшн? Пришел запрос на вебсервис, пошел на абстракцию серч енджайна — вернул данные, где тут виолейшн ССP?

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


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

Гм.


class Some
{
  IDependency _dependency;
}

Вы мне хотите сказать, что у класса Some нет явной зависимости от интерфейса IDependency?


Таким образом и строится архитектура по ДДД, абстракция репозитория в домене, и домен неимеет явных зависимостей на инфраструктуру.

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

Если я не могу его купить, он для меня хуже. Аналогично, если бизнес не можете себе позволить архитектуру, значит, эта архитектура хуже для этого бизнеса, чем другая.
Ну так вы хотите получить объективный ответ на субъективный вопрос. Да, вашем случае хуже, в другом может быть лучше. Как вы хотите получить объективную характеристику в субъективном вопросе.
Вы мне хотите сказать, что у класса Some нет явной зависимости от интерфейса IDependency?

а вы в том же неймспейсе оставьте интерфейс — и не будет зависимости.

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

Почему же, вполне объективный. Наличие ресурсов — это объективный фактор.


а вы в том же неймспейсе оставьте интерфейс — и не будет зависимости.

Никуда она не денется. Не важно, где интерфейс, если интерфейса нет — код не скомпилируется.


Инфраструктура — имеет, домен нет.

Оба имеют.


Домен независим от остальных лееров при этом.

Ну да, от других слоев он теоретически независим. И что?


Я так понимаю, вы на нарушение CCP забили? Оказался неважный принцип?

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

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

«независимость суждений, мнений, представлений и т.п. от субъекта, его взглядов, интересов, вкусов, предпочтений и т.д. (противоположность — субъективность). О. означает способность не предвзято и без предрассудков вникать в содержание дела, представлять объект так, как он существует сам по себе» с словаря — ключевое слово
объект так, как он существует сам по себе
. Дом, САМ ПО СЕБЕ, а не в контексте сколько у вас есть бюджета.

Я так понимаю, вы на нарушение CCP забили? Оказался неважный принцип?
как по мне, там нету виолейшна, сервис один, в себе имеет логику, которая связанна с полнотекстовым поиском.

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

Не на этапе разработки. Но спасибо, кэп, да.


Дом, САМ ПО СЕБЕ, а не в контексте сколько у вас есть бюджета.

Архитектура не существует сама по себе.


как по мне, там нету виолейшна, сервис один, в себе имеет логику, которая связанна с полнотекстовым поиском.

Я то же самое могу сказать про ситуацию, когда вообще все сделано в одном сервисе: "сервис один, в себе имеет логику, которая связана с полнотекстовым поиском". Будет ли это означать, что сделать все в одном сервисе не нарушает CCP?


Независимость слоев — это тоже архитектура. Она дают простоту, изолирует один слой от другого, делает независимость одного слоя от другого

Независимость слоев делает независимость слоев. Угу. Зачем мне это?


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


логически связывает логику в одном. слое.

Неа. Независимость слоев никак не влияет на логическую связность логики в одном слое.

Не на этапе разработки. Но спасибо, кэп, да.
а на этапе продакшна — уже да. Пожалуйста.
Архитектура не существует сама по себе.
почему же?

«сервис один, в себе имеет логику, которая связана с полнотекстовым поиском». Будет ли это означать, что сделать все в одном сервисе не нарушает CCP?
Нет, потому, что в вашем случае логика будет на посыл фидбека. А в моем, только артиклы.

Независимость слоев делает независимость слоев. Угу. Зачем мне это?
Она дают простоту, изолирует один слой от другого, делает независимость одного слоя от другого


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

А на этапе продакшна уже и трогать не надо. Пусть работает.


почему же?

Потому что она призвана выполнять задачу. Без задачи это просто чьи-то размышления о благом.


А в моем, только артиклы.

Во-первых, нет, не только артиклы — еще отправка фидбека по запросам.


А во-вторых, CCP формулируется не в терминах логики, а в терминах причин для изменения: "Gather into components those classes that change for the same reasons and at the same times. Separate into different components those classes that change at different times and for different reasons."


Или это так, несущественные мелочи?

А на этапе продакшна уже и трогать не надо. Пусть работает.
Не совсем так можно «не трогать», могут реквайрменты поменяться, данные имеют свойство расти и т.д.

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

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

Gather into components those classes that change for the same reasons and at the same times. Separate into different components those classes that change at different times and for different reasons
Все правильно, сервис артиклов — отправляет запрос на серч енджайн — выдает респонс. Если отправлять фидбек — то через абстракцию, — это не ответственность артиклов, потому что они бы не имели имплементации фидбеков.
Не совсем так можно «не трогать», могут реквайрменты поменяться, данные имеют свойство расти и т.д.

… и в этот момент снова не понятно, то ли надо имплементацию менять, а то ли интерфейс трогать.


выполнять задачу — это о поведении апликейшна, а архитектура — это о другом

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


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

Вы не понимаете. Он отправляет фидбек о том, какие запросы были сделаны. Он должен это делать.


Все правильно, сервис артиклов — отправляет запрос на серч енджайн — выдает респонс.

"Все правильно" — это сколько у него причин для изменения?

… и в этот момент снова не понятно, то ли надо имплементацию менять, а то ли интерфейс трогать
.Интерфейс -стабильнее, поэтому чаще всего — имплементацию.

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

Вы не понимаете. Он отправляет фидбек о том, какие запросы были сделаны. Он должен это делать
Ну в моем случае будет отдельный сервис(по сути класс с интерфейсом), который будет иметь метод — отправить фидбек. Можно сделать отдельную либу, которая будет отправлять что-то наружу. И заюзать ее. Сервис будет делать тоже — возвращать артиклы, только ещё к тому, на каком-то интерфейсе вызывать _externalInteractor.SendMessage(string message). он не будет имплементить это, он просто вызовет метод. Если нужно будет, потом леер, который отвечает за отправку меседжа можно изменять, дополнять — что угодно. Только это будет проще, нежели искать эту имплементацию в методе, где с статьями работают.

«Все правильно» — это сколько у него причин для изменения?
причина — изменение логики, либо чего-то связанного с серч енджайном. Сервис по себе не зависит от посыла фидбек, ибо он это не имплементит.
Интерфейс -стабильнее, поэтому чаще всего — имплементацию.

Если речь идет об изменении требований, то это, скажем так, не очень предсказуемо. Потому что неизвестно, заложили вы (неизвестные) новые требования в свой интерфейс, или нет.


ааа, я вас неправильно понял. С этим полностью солидарен.

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


Ну в моем случае будет отдельный сервис(по сути класс с интерфейсом), который будет иметь метод — отправить фидбек. Можно сделать отдельную либу, которая будет отправлять что-то наружу. И заюзать ее.

Это никак не повлияет на количество причин для изменения компонента в целом.


причина — изменение логики, либо чего-то связанного с серч енджайном.

Во-первых, это уже две причины, и они происходят в разное время. А во-вторых, вы забыли изменение клиентского контракта, изменение требований на фидбек (=что сохраняется) и изменение контракта с сервисом фидбека. Итого минимум четыре, а на самом деле — больше пяти. Явное, явное нарушение CCP в том виде, в котором он сформулирован.


Я вам больше того скажу, разделение сервисов статей и фидбека — это нарушение первой части CCP ("Gather into components those classes that change for the same reasons and at the same times. ")

… так вот, «должно быть можно разработать нашими силами за месяц» — такая же задача. Если архитектура ее не решает, то это плохая архитектура, негодная.
негодная в одном случае — годная в другом. Это зависит от многих факторов, но сама по себе, она может быть хорошая, хоть и не рентабельна в некоторых случаях.
Это никак не повлияет на количество причин для изменения компонента в целом.
Нет, повлияет. Либа — отдельный, изолированный компонент. Ее с легкостью потом можно поставить в РЕСТ. И тогда — это повлияет. Только вот легче это сделать имея абстракцию, а не лазить по всем артиклам в поисках отправки фидбека.

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

Я вам больше того скажу, разделение сервисов статей и фидбека — это нарушение первой части CCP
Ну и каким образом. Я хочу добавить логику, кроме отправки фидбека наружу, писать все из в бд. Зачем мне нужно изменять что-то в артиклах по этому поводу? То есть изменения на фидбек, которое не трогает артиклы. Мне кажется это не «classes that change for the same reasons and at the same times»
негодная в одном случае — годная в другом.

Так зачем мне архитектура, которая не подходит к моей задаче?


Нет, повлияет. Либа — отдельный, изолированный компонент.

Нет. Только в том случае, если она разворачивается отдельно. а это для обычной библиотеки не так.


Ее с легкостью потом можно поставить в РЕСТ.

Неа, нельзя.


Нет, это причина измения полнотектового поиска, она одна.

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


В случае, если фидбек отдельный сервис, тогда опять таки нет.

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


Фидбек: просто набор меседжа, стринга какая-то и все.

Вот-вот. Сначало было "стринга такая-то", а потом решили добавить кокнретного пользователя, который это сделал. В скольких местах придется поменять?


Ну и каким образом.

Очень простым: вы хотите добавить в контракт фидбека новое поле, см. выше. В скольких местах придется поменять?

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

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

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

Очень простым: вы хотите добавить в контракт фидбека новое поле, см. выше. В скольких местах придется поменять?
ДЖСОООН. Любую модельку сконвертили, и отправили как стрингу.
так не используйте, но от того, что вы её не используете, она хуже не станет.

Повторяю еще раз: архитектура, которую я не могу использовать, для меня плохая. Потому что ни зачем не нужна.


Можно: перемещаешь имплементацию с абстракцией в новый рест сервис, и накатываешь для этого ХТТП запросы.

… а потом выясняешь, что один из параметров запроса — Func<T>, и смотришь, как прекрасно он сериализуется. Или, что проще, видишь, что метод вызывался настолько часто, что из-за дополнительных расходов на HTTP система замедлилась вдвое.


при чем бизнес требования к серч енджайну — он выполняет поиск по текст в статьях и все.

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


Если бы не было бизнес-требований, не было бы и сервиса.


ток киньте это в джсон и отпраьте на рест(в фидбек).

У вас все сервисы между собой общаются по нетипизированным контрактам?


Впрочем, даже если это так, при переходе с JSON-over-HTTP на шину сообщений код придется править в двух местах.

Повторяю еще раз: архитектура, которую я не могу использовать, для меня плохая. Потому что ни зачем не нужна.
Повторяю ещё раз: ну так для вас, в КОНКРЕТНОМ случае плохая. Это не делает её объективно или абсолютно плохой, а только субъективно, в вашем случае.

… а потом выясняешь, что один из параметров запроса — Func, и смотришь, как прекрасно он сериализуется. Или, что проще, видишь, что метод вызывался настолько часто, что из-за дополнительных расходов на HTTP система замедлилась вдвое.Это зависит от нужд проекта.

А то, по каким конкретно полям, и с каким весами? А дополнительные фильтры? Ну и самое главное — реранкинг, сейчас — общий, а в будущем — с учетом пользователя?
Так это и есть его ответственность. Но точно не имплементить фидбеки в себе.

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

на шину сообщений код придется править в двух местах
Почему, так же сериализнул и кинул на какой-нить ребит. Не вижу проблемы.
Повторяю ещё раз: ну так для вас, в КОНКРЕТНОМ случае плохая. Это не делает её объективно или абсолютно плохой, а только субъективно, в вашем случае.

Я не понимаю, как вы оцените объективно хорошую архитектуру. Нету их, потому что любая оценка архитектуры неизбежно привязана к задаче, а задача привязана ко мне.


.Это зависит от нужд проекта.

Вот я и говорю — нельзя просто взять и вынести сборку за REST.


Так это и есть его ответственность.

Да, это его ответственность, она же его бизнес-логика, она же — одна из причин его изменения. Одна, но не единственная.


А что в этом плохого?

Вы это серьезно? Вы серьезно не понимаете, что плохого в нетипизированных контрактах?


Будет моделька конкретная АртиклИнфо какое-нить, её сериализуешь, а далее фидбек делает что хочет.

… например, пытается ее десериализовать в FeedbackMessage, и падает. Это вроде бы не то, что нам надо в системе.


Почему, так же сериализнул и кинул на какой-нить ребит.

(srsly?)


Во-первых, потому что раньше был код, который делал HTTP-запрос, а теперь он пишет в очередь; аналогично на другой стороне раньше был код, который HTTP принимал, а теперь он из очереди читает.


А во-вторых, у вас был синхронный протокол, а стал асинхронный.

Я не понимаю, как вы оцените объективно хорошую архитектуру. Нету их, потому что любая оценка архитектуры неизбежно привязана к задаче, а задача привязана ко мне.
конечно, но есть много похожих архитектур, которые решают похожие задачи. И скорее всего, архитектура не решает одну задачу, а многие. В вашем случае — она может не решать вашу задачу. В другом случае, она может решить чью-то задачу. Так почему же она плохая, только потому что ВАШУ проблему не решает. Другие, для других людей\задач — может решать
Вот я и говорю — нельзя просто взять и вынести сборку за REST.
Ну вот и я говорю, что не можно просто так взять, и запихнуть фидбек в артиклы.

Да, это его ответственность, она же его бизнес-логика, она же — одна из причин его изменения. Одна, но не единственная
как по мне, то единственная.

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

… например, пытается ее десериализовать в FeedbackMessage, и падает. Это вроде бы не то, что нам надо в системе.
зачем? десериализовать надо в Артикл инфо.

(srsly?)
да

Во-первых, потому что раньше был код, который делал HTTP-запрос, а теперь он пишет в очередь; аналогично на другой стороне раньше был код, который HTTP принимал, а теперь он из очереди читает.

А во-вторых, у вас был синхронный протокол, а стал асинхронный.
И?
Так почему же она плохая, только потому что ВАШУ проблему не решает.

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


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

Можно. Ничего не мешает.


ну рест, не типизировнный контракт, и что?

Гм. Можно иметь REST и типизированный контракт при этом.


Нет, вы серьезно не понимаете, что плохого в нетипизированных контрактах?


десериализовать надо в Артикл инфо.

А откуда фидбек-сервису это знать? И как гарантировать, что его структура article info такая же, как у сервиса поиска?

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

Можно. Ничего не мешает.
Ну можно, конечно. Так то взять, то как хочешь пиши. Можно называть переменные в коде (а,b,c). Почему вы так не делаете? Или может и делаете. Может и в этом вы не видите ничего плохого.

Гм. Можно иметь REST и типизированный контракт при этом.
Можно, но зачем. Если можно то, что принял — сразу же и отдать?

А откуда фидбек-сервису это знать? И как гарантировать, что его структура article info такая же, как у сервиса поиска?
Ну можно кинуть референс на артиклы. Будет зависимость. Но артиклы об этом знать не будут.

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

Да нет, это как раз значит, что оно не решает мою проблему.


Можно, но зачем.

Чтобы иметь больше уверенности в совместимости сервисов друг с другом.


Ну можно кинуть референс на артиклы. Будет зависимость. Но артиклы об этом знать не будут.

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


Что конкретно вы имеете в виду под не типизированными контрактами?

Контракты вида "сюда можно послать любой json". Они же — object Invoke(object input).

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

Если мне надо не вкусно, а поесть, еда, которую я не могу купить, не решает мою проблему.


Выполнение ССР на артиклах будет.

Нет, все равно не будет.


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

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

Как отличить правильно спроектированный слой доступа к данным от неправильно спроектированного?
Время покажет.
Угу. Возьмем один метод, [.....] А то же самое между потоками? А между процессами (передача сериализованных данных)?
Вот в этом как раз и проблема всегда. Вы размываете границы между слоями. Вы получили данные из DAL, дальше что вы с ними делаете — не его ответственность. Разные потоки, процессы, да хоть дата центры.
Теперь добавим еще один метод: save(entity).[.....]Что сохранится?
На этот вопрос ответит реализация абстракции TransactionManager
А теперь смотрите, в чем ловушка: совершенно не важно, какие ответы вы дадите. Важно другое — все эти ответы (и десятки других!) явно описаны где-то? На каждый из них есть тест? Или вы просто привыкли, что оно работает так?
На весь функционал, который востребован — да, все описано и покрыто тестами. Иначе подмена реализации чревата проблемами. Да и непонятно как работала первая реализация.
Все остальное уже разработано, протестировано. И трогать не надо ни бизнес логику, ни UI, ни что-то другое. [...] После такого перехода конечно же поменяются характеристики работы приложения
Подождите. Вы же только что говорили, что все уже разработано и протестировано, а теперь говорите, что характеристики поменяются. Если поменялись характеристики — надо перетестировать, а если они стали неудовлетворительными — переписывать. И нет, переписывать только DAL окажется недостаточно. А это значит, ваша абстракция снова протекла.
Вы тут забыли прихватить в цитату окончание фразы
такие как скорость работы, максимальная нагрузка и т.д. и т.п.
Эти вещи обязательно поменяются, т.к. напрямую зависят от характеристик хранилища данных. Но перетестирования всего остального не потребуется. Верстка UI от этого не поломается. Бизнес логика какой была, такой и осталась. Или вы хотите поменять часть приложения так, чтобы ничего не поменялось? Так архитектура не об этом.
Занятно, не находите? Утверждается, что архитектура, которая позволяет поменять хранилище, хорошая, но необходимость поменять хранилище — архитектурный косяк.
Не особо. Есть архитектура приложения, которая позволяет заменить часть приложения, если вдруг это необходимо, без необходимости переписывать вообще всё. И есть архитектурное решение — «мы тут посоветовались и я решил» — используем вот это хранилище. С моей точки зрения эти вещи не связаны. Но если второе вдруг оказалось неверным, то пусть лучше первое будет, чем не будет.
Время покажет.

То есть на момент проектирования — никак? В таком случае задача "сразу правильно спроектировать слой доступа к данным" невыполнима.


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

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


На этот вопрос ответит реализация абстракции TransactionManager

… которой у вас раньше не было, следовательно, ваш изначальный пример уже неполон (и, как следствие, неверен).


На весь функционал, который востребован — да, все описано и покрыто тестами.

Ну то есть сделано очень много работы, применимость которой в будущем под большим вопросом.


Но перетестирования всего остального не потребуется

Потребуется перетестирование системы в целом — а это включает "все остальное".


Так архитектура не об этом.

А о чем тогда "архитектура"?


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

До тех пор, пока разработка "первого" не оказалась настолько дорогой, что до второго вы не дошли.

чтобы замена произошла незаметно и безболезненно?

А если она просто пройдёт менее заметно и менее больно — это уже может быть плюсом абстракции?

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

Суровая реальность допускает некий объём прочности, прежде, чем абстракция протечёт.
Да, все абстракции текут, но это вовсе не повод ими не пользоваться вообще.
А если она просто пройдёт менее заметно и менее больно

Если пройдет — то да.


Да, все абстракции текут, но это вовсе не повод ими не пользоваться вообще.

Я, в общем-то, и не утверждал обратного. Просто надо помнить стоимость абстракции.

Я, в общем-то, и не утверждал обратного. Просто надо помнить стоимость абстракции.

Безусловно! Впрочем, надо так же учитывать и стоимость «прямолинейного кода» (без абстракции), ведь сопровождение кода не бесплатно. И до тех пор пока абстракция не потекла, она предназначена как раз таки облегчать его сопровождение
Впрочем, надо так же учитывать и стоимость «прямолинейного кода»

Конечно, надо. А дальше надо сравнивать несколько величин, а не просто говорить "давайте добавим абстракцию, и все станет лучше".


И до тех пор пока абстракция не потекла, она предназначена как раз таки облегчать его сопровождение

Предназначена, да. Выполняет ли она это предназначение просто тем фактом, что она — абстракция? Нет.

Предназначена, да. Выполняет ли она это предназначение просто тем фактом, что она — абстракция? Нет.

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

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


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

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

"Тима", которая это пишет — из одного человека, там работы на две недели. С кем коммуницировать-то?

это хорошо работает, пока человек один

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

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

Поэтому они садятся и обсуждают, как лучше выделить абстракцию

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

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

Вопрос: какой набор функциональности будет предоставлять абстракция — тот, который есть у используемой нами сейчас системы, или какой-то другой?

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

О. А вы говорите — они всегда упрощают.


Поэтому они садятся и обсуждают, как лучше выделить абстракцию

Проблема в том, что когда разработку вели, обсуждать было не с кем, человек один был. Не будешь же дергать коллег/тимлидов ради каждой абстракции в коде?


Нет, не по этому, а потому что старая абстракция (или её отсутствие) нас чем-то не устроила

Ненене, нас всё всем устраивало. Но почему-то утверждается, что сам факт введения абстракции облегчит сопровождение.


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

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

О. А вы говорите — они всегда упрощают.

«Зачем упрощать, ведь мне (а больше никого и нет) и так всё понятно» — примерно такой аргумент. Но это тема отдельного разговора) Пока человек один и со всем справляется в разумные сроки — и напрягаться особо не надо)

Не будешь же дергать коллег/тимлидов ради каждой абстракции в коде?

Не вижу в этом проблемы. Обычно перед началом разработки это обсуждается

Ненене, нас всё всем устраивало. Но почему-то утверждается, что сам факт введения абстракции облегчит сопровождение.

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

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

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

Нет, аргумент другой: не всякая абстракция упрощает. Это мой личный опыт.


Не вижу в этом проблемы.

А я вижу. Это время и ресурсы.


Обычно перед началом разработки это обсуждается

Все заранее не обсудишь.


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

Ну так вами же утверждается, не мной.


разве просто сделать селф-чек: а не пора ли тут ввести абстракцию или пока и так норм?

Вот как отличить "норм" от "не норм"?


Прям интересно стало, что это за такой набор функций особенный в логгере

Это уже второй вопрос. Если бы используемые нами сервисы не отличались друг от друга, мы бы их не меняли, правда?

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

И абстракция, которая скрывает то, что ты делал, усугубляет эту ситуацию.


Вспомнить будет намного легче,

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


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

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

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

Для связывания — не нужна. С легкой заменой все тоже не так просто — смотри пример с логгером.


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

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

Ой, да ну! Ну вот вы хотите вынести работу с sql-базой данных во внешний модуль, чтобы каждый раз не повторять все эти установки коннекшенов до базы, курсоры, экранизацию запросов. Логически изолированый кусок кода? Да. Абстракция? Тоже да
Логически изолированый кусок кода? Да. Абстракция? Тоже да

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

Если вы под абстракцией понимаете любое вынесение кода

Любое вынесение (читай: сокрытие) неважного в данном контексте кода, да.

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

Почему это? Код вынесен? Чтению и пониманию, того, что сейчас важно не мешает? Уже хорошо
Любое вынесение (читай: сокрытие) неважного в данном контексте кода, да.

Ну то есть любой extract method. Я боюсь, что наше с вами понимание не совпадает тогда, да. Неудобно.


Почему это? Код вынесен?

Потому что она больше не позволяет "легко заменить реализацию". И тестирование не упрощает.

Потому что она больше не позволяет «легко заменить реализацию». И тестирование не упрощает.

Да можно заменить реализацию, если интерфейс позволяет. (И, по опыту, он позволяет на достаточно протяжённом этапе это делать, а не чуть что сразу меняется)

И абстракцию как раз таки проще тестировать, потому что явно выделяются те важные части, которые хочется покрыть тестами в первую очередь (публичный интерфейс)
Да можно заменить реализацию, если интерфейс позволяет

Какой "интерфейс", если мы говорим об обычном extract method refactoring, который все, что делает, это выносит код в отдельный метод?


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

Это если вы выделяете публичный интерфейс. Что для "любого вынесения кода" неверно.

Какой «интерфейс», если мы говорим об обычном extract method refactoring, который все, что делает, это выносит код в отдельный метод?

А что, у метода нету интерфейса по-вашему? Какие он параметры в себя принимает, какие — возвращает

Это если вы выделяете публичный интерфейс. Что для «любого вынесения кода» неверно.

Но тем не менее, отдельный метод проще тестировать, потому что внезапно понимается что это за метод (если конечно вы не бездумно вынесли «какой-то» участок кода, назвав его чем-нибудь типа «method_4_1, но это уже сюр какой-то), и что он должен делать, а, соответственно, и как проверить, что он делает ровно то, что он должен
А что, у метода нету интерфейса по-вашему? Какие он параметры в себя принимает, какие — возвращает

Как вы предлагаете "заменить реализацию" метода?


Но тем не менее, отдельный метод проще тестировать

Только если он (а) публичный и (б) не имеет неожиданных зависимостей.

Как вы предлагаете «заменить реализацию» метода?

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

все подобные изменения ну никак не меняют интерфейса метода

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

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

А что это тогда?
Вот один код у вас реализует конкретный алгоритм. Вот вы переписываете этот код, и он теперь реализует другой алгоритм. Вы одну реализацию заменили на другую.

Теперь мне даже интересно, что же Вы имели в виду под этим термином)
Не всякая абстракция упрощает. Это мой личный опыт.

Было бы интересно узнать подробности: какая была выбрана абстракция, почему, и почему она не сработала (и сразу ли она «не сработала»)

А я вижу. Это время и ресурсы.

Это потребует меньше времени, чем на последующие переделки

Вот как отличить «норм» от «не норм»?

Кажется, мы ходим по кругу) По личным ощущениям, по к-вам багов в данном участке кода, по времени исправления / внесения изменений в этом участке кода

почему она не сработала

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


Это потребует меньше времени, чем на последующие переделки

Это неизвестно, на самом деле.


По личным ощущениям, по к-вам багов в данном участке кода, по времени исправления / внесения изменений в этом участке кода

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

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

Всё ещё никаких подробностей) Какие «любые» изменения? Почему никакой пользы? Зачем Вы её вводили тогда?

Это неизвестно, на самом деле.

Ну, мой опыт, показывает, что зачастую всё-таки так

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

Ну, да… Но ведь книжки — это по сути ещё чей-то опыт, который может быть полезным (а может и нет)
Зачем Вы её вводили тогда?

Не я, предыдущий разработчик. Потому что он думал, что абстракции — это хорошо и полезно.


Но ведь книжки — это по сути ещё чей-то опыт, который может быть полезным (а может и нет)

Вот вопрос того, в какой мере, и как это удается применять.

Вот вопрос того, в какой мере, и как это удается применять.

Ну это больше проблема не автора, а читающего и интерпретирующего прочинанное

А у меня к автору книги претензий-то и нет особо.

Это все к тому, что красивые фразы «это все детали реализации» обычно так и остаются красивыми фразами, потому что суровая реальность намного сложнее, чем простые примеры.


"Абстракции обычно так и остаются красивыми фразами" (читай: не работают почти всегда) — это ни что иное, как претензия к автору книги, который, в какой-то её части за эту идею выступает

Я не люблю спорить с книгой, которую я не читал.

Я бы сказал обратное, судя по тому, сколько вы уже сдесь пишите)
Лучше почитайте Принципы, паттерны и методики гибкой разработки на языке C, от того же Мартина, то же самое, но букв намного больше. Смысл пересказывать то, что можно прочитать за пару-тройку дней
Согласен, смысл только в том, если хочешь решить надо ли читать или нет, и в том, что если у тебя нету этих 3 дней — то основные мысли, кроме раскрытия темы SOLID'a — я передал тут.

Что касается этой книги — в планах почитать уже давно, нужно только время, спасибо за рекомендацию
Заинтересовался, пошел искать. Но оказалось, что там язык не С, а С#.
Если вы считаете что Single responsibility principle — это про то, что класс, или же модуль должен делать только что-то одно — то вам обязательно нужно прочитать хоть бы главу про Солид. Ибо то определение, что дано выше — это следствие, но никак не определение самого принципа

Не "делать что-то одно", а делать что-то логически связное так, чтобы потребитель использовал это целиком, но не по частям.


Например, внезапно, вы решите использовать вместо Postgresql MongoDb, или вообще файлики, или использовать mock’нутые данные, операции с которыми будут производится в памяти. И при некоторых условиях — это может заставить переписать почти всю логику.

Уверены что будет меняться — делаете интерфейс. Затупливаете и не можете выбрать — делаете интерфейс, но сначала убедитесь что в этот интерфейс лягут возможные реализации. С другой стороны, иногда хорошо именно завязаться на тот же PostgreSQL потому что иначе мы ограничим себя очень небольшим набором возможностей СУБД, который допустимо использовать. Но тут уже нужно быть уверенным.


Дядя Роб, также рассказывает о том, как детали реализации могут навредить вашей системе, и не дать эволюционировать без боли в дальнейшем.

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

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


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

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

Нужны 4 сильных программиста, занести компьютерные столы в кабинет. © завхоз универа

На прошлом месте работы это называли "работой по специальности"

Роберт Мартин в своей книге пишет про набор принципов и подходов.
Это не значит что взяв любой из пунктов, и бездумно применив его, можно сразу сделать хорошо.
Там выше в комментах была активная дискуссия, насколько легко/тяжело менять одну БД на другую.
Тогда как Мартин акцентирует внимание не на легкости смены одной БД на другую. А на том, что важные решения нужно принимать при наличии фактических данных, т.е. как можно позже. По сути, речь идет о том, что выпускай релиз как можно раньше, потом смотри какая у тебя нагрузка, что болит, и только потом думай, как с этим правильно работать.

И в целом пропагандирует не легкость замены одной БД на другую, а наличие четких тонких границ между компонентами. Да, в качестве примера приводится в том числе возможность замены БД, но это только пример правильных границ. Да и сами правильные границы не являются самоцелью. Цель — управляемость изменений.
Sign up to leave a comment.

Articles