Комментарии 45
Да не получится так думать на самом деле. Это допустимо лишь в том случае, когда переносимость — изначальное бизнес-требование к проекту. Во всех остальных случаях ответ на вопрос «что к чему прибивать гвоздями» всегда имеет численное выражение в человеко-часы и деньги, и должен быть решён одним из первых, на самых ранних этапах проектирования.
Такая архитектура нужна не только для переносимости между платформами. Пример из своей области (Android) — нужно какую-то ленту новостей отображать. Клепаешь что-то на коленке. Чтобы побыстрее выкатить MVP кэширование в базу не впиливаешь, а каждый раз подгружаешь с инета. Но потом таки наступает момент, когда и кэш можна уже впилить, — а у тебя GUI знает, что данные с инета идут. Вот тут ты и попал.
YAGNI — очень хороший принцип, чесслово. Добавляйте гибкость там, где вы точно знаете когда вы будете её использовать. Если у вас в следующем релизе запланировано кеширование данных с бекенда — дело одно, а если вы это закладываете «на всякий случай» — совсем другое.
которым изначальный выбор %DATABASE_NAME% не подешел
это значит что на момент выбора базы данных у разработчика не-было ни малейшего представления что он собирается сделать.
Приведу пример. Например делали мы проект на mysql и все было замечательно. А потом всплыла работа с геоданными и т.д. Не вопрос, ставим рядом elasticsearch, дублируем туда данные (только то что нужно) и используем их для построения необходимых агрегаций.
Ну то есть если для конкретной задачи ваша база данных не подходит — это не повод "менять все", это лишь повод поставить рядом ту что подходит и как-то решить проблему малой кровью. Когда все "прибито гвоздями" это будет дороже, а когда есть пространство для маневра (DAO, Table Gateway, Data Mapper) — все довольно легко решается. Ну а если база данных не подходит для доброй половины задач — ну явно что-то не так пошло. Значит разработчик не разобрался зачем оно ему надо. И сейчас это проблема — многие берут тулзы не потому что они им нужны — а потому что в тренде все.
А всего-то надо разобраться с простейшей идеей разделения ответственности. И жить станет проще.
С временем жизни проекта количество платформ может увеличиться, ядро программы должно общаться с любой из них одинаковым образом. Ядро программы может вообще пережить выбранную платформу.
А что в этом, собственно, плохого? Если платформа позволяет решить задачу так, как надо, и нет потребности делать переносимое решение, то почему бы и не сделать его платформенно-зависимым?
> С временем жизни проекта количество платформ может увеличиться
> Ядро программы может вообще пережить выбранную платформу.
А может и не увеличиться, а может и не пережить. И у кросс-платформенного решения есть своя цена. Причём она может выражаться не только в упомянутых выше деньгах и человеко-часах. Это может быть и цена производительности решения, и его масштабируемости, и его сроках выхода на рынок. Поэтому я не могу утверждать, что такая архитектура лучше или хуже. Всё, что я могу утверждать — что сначала надо делать оценку, а потом решать, подходит она конкретно для вашего проекта, или нет.
Ядро программы может вообще пережить выбранную платформу.
А еще ядро может исчезнуть. Микросервисы нынче в тренде и все такое. Бац так и нет ядра — распределенное приложение со всеми плюсами и минусами.
От себя внёс бы правку вместо реплики «Ну, возможно, поймешь лет через десять» привёл бы пример про недостаточность данных в начале разработке. Когда просят одно, а потом оказывается, что это немного другое. Или пока идёт разработка бизнес логика может измениться (например, вслед за потребностями рынка). И поэтому следует принимать эти решения, намного позже и делать всё оптимально гибко.
Хороший текст. Много букв, объясняющих простую мысль — архитектуру не проектируют. Архитектура растет, развивается и преобразуется вместе с ростом кодовой базы. И самое сложное в работе архитектора — не загнать самого себя в угол, построив то, что невозможно будет изменить в нужную сторону. А с другой стороны гибкость = сложность. А сложность означает высокую стоимость внесения изменений, что приводит в тот же угол. Примерно так.
А с другой стороны гибкость = сложность. А сложность означает высокую стоимость внесения изменений, что приводит в тот же угол.
Вот очень хорошая фраза. Но проблема как мне видится в том что слова сложность и гибкость воспринимаются разработчиками оочень по разному.
Например штуки вроде инкапсуляции призваны понижать сложность и в целом положительно влияют на гибкость системы. С другой стороны если сильно загоняться по инкапсуляции может возникнуть ситуация с увеличением связанности между компонентами. И уже надо искать баланс.
Так же есть еще такая штука… вот вроде взять и изолировать работу с базой в отдельном объекте (gateway) — это не сложно. Это не то что бы сильно дольше, и это не требует каких-то глубоких познаний в программировании. Но "размазать по контроллерам же проще". Нет у людей понимания что проще — это когда мысль заложенная кодом прослеживается явно. "Простые" решения частенько приводят к "сложным" проблемам.
Ладно… я походу упоролся… но мне кажется вопрос сложности в ПО крайне интересным потому как я часто замечаю среди неопытных разработчиков извращенную трактовку сложности.
Многовато воды. 15 реплик, прежде чем привести пример кода, вместо того, чтобы сразу сказать "интерфейсы!"
А при проектировании "архитектором" интерфейса хранения данных неплохо бы иметь в виду какой-нибудь вариант реализации (опыт использования конкретных СУБД или создания файлового хранилища), чтобы знать, что придуманный интерфейс реализовать можно.
Иначе может получиться, что интерфейс-то есть, реализацию написали, а использовать систему нельзя — слишком плохо/медленно/etc всё работает из-за интерфейса, который в принципе нельзя реализовать эффективно.
Ещё опасно надеяться на то, что где-то есть реализация нужных фич (функциональности/надёжности/скорости), или что эти фичи как-то можно реализовать, не зная наверняка, как.
Благословенна команда, чьи архитекторы предоставили способ откладывания всех этих решений до момента, когда у команды будет достаточно информации для их принятия.
Могу предположить, что так не бывает, хотя к этому и надо стремиться.
Информация поступает уже после реализации. Иногда поступает такая информация, что приходится выкидывать все интерфейсы, реализацию, клиентский код и горе-архитектора, чтобы переписать всё нормально, чтобы этим можно было пользоваться.
До следующей подобной итерации. Это немного печально, но в этом суть ПО — его (как правило) можно переписать.
В идеале, да.
С другой стороны, делать полностью независимые от платформы/языка подсистемы — это ещё более "java-way" (очень суровый энтерпрайз, в смысле). Может получиться слишком абстрактно для обучения. Да и для жизни тоже.
Акцент делался на инверсии зависимостей, а не на фиксации платформы. В фиксации платформы нет ничего плохого, но вот сегодня у вас бизнес правило (или ограничение) реализуется в рамках вашей системы, а завтра вы решили реализовать в микросервисе. И проблема та же.
да и не умея писать нормальные монолиты микросервисы делать не стоит. Проблем будет больше.
Вы просто не учитываете тот момент, что зачастую изменения можно, а иногда и нужно игнорировать. Да, работая с каким-то вендорским сервисом, вам деваться некуда — это будут решать за вас, и вам для вашей же безопасности потребуется прослойка, которая абстрагирует вас от изменений API (насколько это возможно). Но если речь идёт, например, о СУБД или какой-либо библиотеке, то тут у вас уже больше вариантов для манёвров. Нередко оказывается, что ценность ПО и/или обрабатываемых им данных на порядки выше, чем ценность миграции на новые версии. И в таких случаях, например, может быть правильнее пожертвовать переносимостью в угоду стабильности ядра приложения.
Если можно — Oracle или MS, мне тоже интересно послушать.
> даже самой совершенной платформы
Я по этому поводу с вами совершенно согласен… но от этой проблемы, в общем-то, механизма защиты вообще нет. У лидеров проектов с взаимозаменяемостью крайне плохо. Если команда очень большая, и можно выдвинуть нового лидера, считай, повезло.
Вы, на самом деле, приводите отличный контрпример :) Это система, которая была изначально жестко привязана к одной файловой нативной СУБД. И когда пришло время и спрос, её «отвязали» и дали возможность использовать другие СУБД.
не забывайте еще о языке программирования. Вы на него завязались и уже никуда не слезите. И абстрагироваться от оного у вас не выйдет (только микросервисы разве что спасают но они привносят тот уровень сложности, который оправдан на малом проценте проектов).
Базы данных — так же. Нет смысла полностью от них абстрагироваться, но отделять логику от оных — вполне. То есть банальный DAO решает проблему так же как и data mapper или active record (да да, если обернуть data-model в обертку предоставляющую объектную модель, что-бы наша бизнес логика использовала active record тупо как атрибуты, то тогда все хорошо и плохо только с точки зрения юнит тестирования, и то есть варианты).
Менять базы данных никто обычно не планирует. А вот добавлять еще — это можно. Вполне может возникнуть ситуация при которой придется хранить часть данных в другой СУБД которая просто эффективнее справляется с задачей. А еще есть CQRS что бы творить вообще магические вещи. А еще — Event Sourcing что-бы совсем абстрагироваться от хранилища (на запись во всяком случае).
У того же FB есть проблемы с "прибитыми гвоздями" решениями. Часть они решают (HHVM, Hack вместо php), часть — нет (long polling вместо websockets) ну и т.д. Но у них и ресурсов побольше и задачи другие.
А AWS используют микросервисы что-бы скейлиться. Да и при их масштабе по другому уже не выйдет. Опять же микросервисы это эдакий способ снижения связанности. Вы всегда можете критическое место переписать за адекватные строки без ущерба проекту.
Есть некоторое недопонимание у комментирующих.
Эта архитектура не про про переносимость.
Эта архитектура позволяет откладывать принятие решений.
Вы начинаете разрабатывать ваше приложение(бизнес логику) и постепенно формируете требования, например, у хранилища должны быть транзакции, нужна возможность полнотекстового поиска. Так вы сформулируете абстракцию хранилища. Естественно, вы будете оглядываться на существующие решения или их комбинации.
Эта архитектура про независимость от реализации, а не про независимость от абстракции. Еще раз: ядро будет зависеть от абстракции.
Ваше ядро будет знать, что вы работаете с чем-то MySQL подобным. Но не будет знать что это конкретно MySQL или заглушка с тем же интерфейсом, но хранящая данные в памяти.
Т.к. ядро зависит от MySQL-подобной абстракции, то не получится просто так перейти на другую абстракцию вроде Key-Value или EventSourcing.
Аналогично можно сказать и про UI и про все остальное.
Я имел ввиду, что есть абстракция(интерфейс) "Ноги": "левую вперед", "правую вперед", и т.д.
А какие конкретно ноги — это приходит извне.
Это не тоже самое, что абстракция "Перемещатель". Где у "Перемещателя" есть метод переместить в точку. А "перемещателелем" могут быть и ноги и машина и самолет.
Соответственно, эта архитектура о том, что вы знаете, что вы управляете "Ногами", но какими именно не знаете.
https://8thlight.com/blog/uncle-bob/2011/11/22/Clean-Architecture.html
Is this a perfect scheme? Of course not. Might one kind of UI be so different from another that they couldn’t share a common interface? Of course that’s possible. Does that mean this kind of decoupling is a waste? Are you kidding me?
https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the an inner circle. That includes, functions, classes. variables, or any other named software entity.
Никто не заставляет вас делать "универсальную абстракцию" БД или UI. Просто нужно сделать так, что бы БД или UI были плагинами к ядру. Естественно, этот интерфейс/абстракция располагается в ядре. Значит, этот интерфейс должен удовлетворять требованиям проекта. Если приложение работает с таблицами, то естественно, ему не подойдет Key/Value хранилище.
ну то есть инверсия зависимостей. То что наши классы реализующие работу с хранилищами завязаны на конкретной базе данных — это нормально если логика абстрагирована от конкретного хранилища интерфейсом. Простые примеры — Dao, Table Data Gateway и т.д. Или то что UI у нас завязан на приложении — это нормально. Как никак изменения в логике полюбому вызывают изменения в UI или хранилище (в пределах разумного конечно). А вот изменения в UI никак не должны сказаться на логике работы приложения.
И в совокупности всего этого можно получить хорошую архитектуру которая не только гибкая, ускоряет процесс разработки, имеет стандартные архитектурные паттерны, то есть новый разработчик быстрее вольется в разработку. Все это я к тому что архитектура может и должна выдавать не только гибкость.
Маленькая архитектура