Помогает ли разделение потенциально монолитного приложения на несколько более мелких приложений предотвратить ошибки? [закрыто]

48

Другой способ спросить это; почему программы имеют тенденцию быть монолитными?

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

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

DNV
источник
9
If the animation and modelling capabilities were split into their own separate application and developed separately, with files being passed between them, would they not be easier to maintain?Не смешивайте, проще расширять с более легким в обслуживании модулем - ведь он не свободен от сложностей или сомнительных конструкций. Майя может быть адом на земле, чтобы поддерживать, в то время как его плагины - нет. Или наоборот.
LAIV
37
Я добавлю, что одну монолитную программу легче продать , и большинству людей ее проще использовать .
Дартфеннек
2
@DarthFennec Лучшие приложения для пользователя выглядят как одно приложение, но используют все, что нужно, под капотом. Сколько микросервисов обеспечивают работу различных веб-сайтов, которые вы посещаете? Почти ни один из них больше не является монолитом!
CorsiKa
23
@corsiKa Как правило, ничего не получится, написав настольное приложение в виде нескольких программ, которые взаимодействуют друг с другом, а не просто путем написания нескольких модулей / библиотек и их объединения в монолитный двоичный файл. Микросервисы служат совершенно другой цели, поскольку они позволяют одному приложению работать на нескольких физических серверах, что позволяет масштабировать производительность с нагрузкой.
Дартфеннек
5
@corsiKa - я думаю, что подавляющее количество веб-сайтов, которые я использую, по-прежнему монолиты. В конце концов, большая часть интернета работает на Wordpress.
Давор Адрало

Ответы:

94

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

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

Монолитное приложение - это действительно вариант по умолчанию, с которым вы сталкиваетесь, когда добавляете все больше и больше функций в одно приложение. Это самый простой подход, когда вы рассматриваете каждую функцию отдельно. Только после того, как оно станет большим, вы можете посмотреть на все и сказать: «Вы знаете, что, это будет работать лучше, если мы отделим X и Y».

Ewan
источник
6
Да, и есть также соображения производительности, например, стоимость передачи указателя по сравнению с сериализацией данных.
Джимми Джеймс
65
«Как правило, 2 небольших и менее сложных приложения гораздо проще поддерживать, чем одно большое». - это правда, кроме случаев, когда это не так. Зависит от того, где и как эти два приложения должны взаимодействовать друг с другом.
Док Браун
10
«Как правило, 2 небольших и менее сложных приложения гораздо легче обслуживать, чем одно большое». Я думаю, что я хочу больше объяснений этому. Почему именно процесс генерации двух, а не одного исполняемого файла из базы кода волшебным образом облегчает код? Что решает, насколько легко рассуждать о коде, так это насколько тесно он связан и тому подобные вещи. Но это логическое разделение и не имеет ничего общего с физическим .
Во
11
@Ew Физическое разделение не вызывает логического разделения, вот в чем проблема. Я могу легко спроектировать систему, в которой два отдельных приложения тесно связаны. Конечно, здесь есть некоторая корреляция, так как люди, которые тратят время на разделение приложения, скорее всего, достаточно компетентны, чтобы рассмотреть эти вещи, но нет никаких причин предполагать какую-либо причинность . По той же логике я могу утверждать, что использование последней версии C # значительно упрощает поддержку кода, поскольку команда, которая постоянно обновляет свои инструменты, вероятно, также будет беспокоиться о поддержке кода.
Во
9
Я думаю, что обсуждение здесь можно суммировать с помощью двух утверждений: 1) Разделение самого приложения не делает приложение более удобным для обслуживания - напротив, оно обеспечивает другую возможную точку отказа 2) Разделение приложения заставляет задуматься о том, где разделить это, которое обеспечивает преимущество по сравнению с монолитом, где это никогда не делалось.
Р. Шмитц
51

Помогает ли разделение потенциально монолитного приложения на несколько меньших, предотвратить ошибки

Вещи редко так просты в реальности.

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

Тем не мение,

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

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

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

Короче говоря, это часто компромисс, и ничего, где ответ «да» или «нет» не является правильным в целом.

почему программы имеют тенденцию быть монолитными

Они? Посмотрите вокруг, в мире есть миллионы веб-приложений, которые не выглядят для меня монолитными, как раз наоборот. Есть также много доступных программ, которые предоставляют модель плагина (AFAIK, даже программное обеспечение Maya, которое вы упомянули).

не будет ли их легче поддерживать

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

Док Браун
источник
4
В последнем предложении закон Конвея гласит, что структура системы имеет тенденцию имитировать орг. структура: разработчики / команды лучше знакомы с некоторыми частями, чем с другими, поэтому, хотя исправления / улучшения должны происходить в наиболее релевантной части, разработчику может быть проще взломать его на «свои» части, чем (а) узнать, как это другая часть работает или (б) работает с кем-то более знакомым с этой частью. Это связано с «швами», о которых упоминает @TKK, и с тем, как трудно найти и применить «правильные» / простые.
Варбо
38

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

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

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

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

Voo
источник
Но что, если вы возьмете настольное приложение с несколькими режимами редактирования и вместо этого просто создадите одно настольное приложение для каждого режима, который пользователь будет открывать индивидуально, а не через интерфейс. Разве это не устранит нетривиальный объем кода, предназначенного для создания «функции» «пользователь может переключаться между режимами редактирования»?
Великая утка
3
@TheGreatDuck Похоже, это также устранит нетривиальное количество пользователей, которым не нравится переключаться между различными приложениями. ;) Но да, исключение функций обычно приводит к упрощению кода. Отмените проверку орфографии, и вы исключите возможность наличия ошибок проверки орфографии. Это просто редко делается, потому что функция была добавлена, потому что кто-то хотел ее.
Одалрик
1
@TheGreatDuck Конечно, дизайн UX должен предшествовать любым архитектурным решениям. Нет смысла иметь лучшую архитектуру, если никто не использует вашу программу. Сначала решите, что вы хотите построить, и основываясь на технических деталях. Если два отдельных приложения предпочтительнее, пойти на это. Вы все еще можете поделиться большим количеством кода через общие библиотеки.
Во
Правда ли, что сложность системы связана с жестким соединением деталей? Я хотел бы сказать, что общая сложность возрастет, если вы разделите свою систему при введении косвенного обращения и связи, хотя сложность отдельных отдельных компонентов изолирована в ограниченном состоянии более ограниченной сложности.
Алекс
1
@TheGreatDuck Основное предположение здесь заключалось в том, что системы имеют что-то общее и на самом деле должны так или иначе взаимодействовать друг с другом. Я не думаю, что ОП спрашивал о том, было бы проще поддерживать два совершенно разных приложения, которые связаны по какой-то странной причине, если бы они были разделены. Похоже на странный крайний случай, который редко встречается на практике (хотя я уверен, что кто-то где-то это сделал).
Во
15

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

Нет U
источник
отличный пост. я думаю, что классический / канонический пример того, о чем вы говорите, это приложение с графическим интерфейсом. во многих случаях приложение с графическим интерфейсом - это одна программа, а бэкэнд / интерфейс тесно связаны. со временем возникают проблемы ... как будто кто-то другой должен использовать бэкэнд, но не может, потому что он привязан к внешнему интерфейсу. или обработка бэкэнда занимает слишком много времени и затягивает внешний интерфейс. часто одно большое приложение с графическим интерфейсом пользователя разделяется на две программы: одна - это графический интерфейс пользователя, а другая - бэкэнд.
Тревор Бойд Смит
13

Важно помнить, что корреляция - это не причинно-следственная связь.

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

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

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

Чтобы процитировать CAR Hoare:

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

Если это так, зачем кому-то строить излишне сложное или монолитное решение? Хоар дает ответ в следующем предложении:

Первый способ гораздо сложнее.

А позже в том же источнике (лекция премии Тьюринга 1980 года):

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

Даниэль Приден
источник
6

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

Как правило, хорошо написанное монолитное приложение является эффективным. Межпроцессное взаимодействие между устройствами не дешево. Разбиение одного процесса снижает эффективность. Однако выполнение всего на одном процессоре может привести к перегрузке процессора и снижению производительности. Это основная проблема масштабируемости. Когда сеть входит в картину, проблема становится более сложной.

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

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

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

MarvW
источник
4

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

Почему?

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

    • общий стиль пользовательского интерфейса, механизмы взаимодействия и т. д. Итак, теперь у вас есть проблемы с дизайном. (Как команды разработчиков снова общаются?)
    • обратная совместимость (можно ли импортировать модельер v1 в аниматор v3?)
    • облачная / сетевая интеграция (если она есть) теперь должна обновляться в два раза больше продуктов.
  • Теперь у вас есть три потребительских рынка: модельеры, аниматоры и аниматоры модельеров

    • У них будут противоречивые приоритеты
    • У них будут противоречивые потребности в поддержке
    • У них будут противоречивые стили использования
  • Должны ли аниматоры Modeller открывать два отдельных приложения для работы с одним файлом? Есть ли третье приложение с обеими функциями, загружает ли одно приложение функции другого?
  • и т.д...

При том, что меньшие базы кода проще поддерживать на уровне приложений, вы просто не получите бесплатный обед. Это та же проблема, которая лежит в основе Micro-Service / Any-Modular-Architecture. Это не панацея, сложность обслуживания на уровне приложения обменивается на сложности обслуживания на уровне оркестровки. Эти проблемы все еще остаются проблемами, их просто больше нет в кодовой базе, их нужно либо избегать, либо решать.

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

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

Kain0_0
источник
3

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

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

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

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

CL40
источник
2
Моя первая работа программистом была программистом с ошибками тысячелетия. Программное обеспечение, над которым я работал, было разбито на сотни маленьких программ, которые все делали небольшую часть, соединялись вместе с пакетными файлами и использовали файлы для передачи информации о состоянии. Это был большой беспорядок, изобретенный в то время, когда компьютеры работали медленно, имели мало памяти и хранилище было дорого. Когда я работал с ним, коду было уже 10-15 лет. Как только мы закончили, они попросили моего совета, и мой совет состоял в том, чтобы преобразовать все в новое монолитное приложение. Они сделали, и через год я получил большое спасибо.
Питер Б
@PieterB У меня был похожий опыт. Технология "ультрасовременных", к сожалению, во многих отношениях является очень большим культом груза. Вместо того, чтобы выбирать лучший метод для работы, многие компании просто следуют тому, что FAANG делает в то время, без каких-либо вопросов.
CL40
а также: то, что может выглядеть как монолитное приложение после компиляции, может быть очень модульным, с точки зрения кода.
Питер Б
1

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

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

Если единицы данных, с которыми работает программа, большие (то есть изображения), то любые задержки между процессами будут длиннее и труднее устранить - что-то вроде «применить преобразование к 10 МБ изображению» мгновенно получит + 20 МБ дискового / сетевого ввода-вывода в дополнение 2 преобразования из формата в памяти в сериализованный формат и обратно. На самом деле вы мало что можете сделать, чтобы скрыть время, необходимое для этого от пользователя.

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

Расщепление монолитной «программы» светит там, где задержки связи не являются критическими или уже неизбежны

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

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

Алексей Левенков
источник
0

Помогает ли это предотвратить ошибки?

Предотвращать? Ну нет, не совсем.

  • Это помогает обнаруживать ошибки .
    А именно, все ошибки, о которых вы даже не подозревали, которые вы обнаружили только тогда, когда пытались разбить весь этот беспорядок на более мелкие части. Таким образом, в некотором смысле, это предотвратило появление этих ошибок в производстве - но ошибки уже были.
  • Это помогает уменьшить влияние ошибок .
    Ошибки в монолитных приложениях могут повредить всю систему и вообще не дать пользователю взаимодействовать с вашим приложением. Если вы разделите это приложение на компоненты, большинство ошибок - по дизайну - затронут только один из компонентов.
  • Это создает сценарий для новых ошибок .
    Если вы хотите, чтобы взаимодействие с пользователем оставалось неизменным, вам нужно будет включить новую логику для взаимодействия всех этих компонентов (через службы REST, через системные вызовы ОС, что у вас есть), чтобы они могли беспрепятственно взаимодействовать с POV пользователя.
    В качестве простого примера: ваше монолитное приложение позволяет пользователям создавать модели и анимировать их, не выходя из приложения. Вы разделяете приложение на два компонента: моделирование и анимация. Теперь ваши пользователи должны экспортировать модель приложения моделирования в файл, затем найти файл и затем открыть его с помощью приложения анимации ... Посмотрим правде в глаза, некоторым пользователям это не понравится, поэтому вы должны включить новую логику для приложение для моделирования для экспорта файла иавтоматически запустите приложение анимации и откройте файл. И эта новая простая логика может содержать множество ошибок, касающихся сериализации данных, доступа к файлам и разрешений, пользователей, меняющих путь установки приложений и т. Д.
  • Это идеальное оправдание для применения столь необходимого рефакторинга .
    Когда вы решаете разделить монолитное приложение на более мелкие компоненты, вы (надеюсь) делаете это с гораздо большим знанием и опытом работы с системой, чем когда она была впервые разработана, и благодаря этому вы можете применить ряд рефакторингов для создания кода. чище, проще, эффективнее, эластичнее, безопаснее. И этот рефакторинг может, в некотором смысле, помочь предотвратить ошибки. Конечно, вы можете также применить тот же рефакторинг к монолитному приложению, чтобы предотвратить те же ошибки, но вы не делаете этого, потому что это настолько монолитно, что вы боитесь прикоснуться к чему-либо в пользовательском интерфейсе и нарушить бизнес-логику ¯ \ _ (ツ) _ / ¯

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

Walen
источник