Офер за 1 день в команду BetterMe (Frontend Hiring, JavaScript/React/Redux)
×Закрыть

Ошибки в архитектуре ПО и как их избежать. Часть 1

По просьбе DOU IT-специалисты поделились ошибками, с которыми приходилось сталкиваться, в построении архитектуры ПО, выборе технологий, их использовании. Всего мы собрали 11 кейсов. В первой части рассмотрим случаи о несоответствии шаблона проектирования требованиям, об Event driven state machine, неправильной настройке ORM и прочем.

Иллюстрация Алины Самолюк

Орхан Гасымов, Digital Transformation Architect в GlobalLogic

Выбор технологий на основе личных преференций

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

Одна из задач проекта — выбрать базу данных. Так как проект не был большим, планировали использовать одну БД. Команда озвучила несколько вариантов, включая SQL и NoSQL. Тимлид очень хотел использовать именно NoSQL, с которой ему было интересней работать — MongoDB, чтобы не нормализовать данные и хранить JSON-массивы. При этом его аргументы «за» основывались на информации из интернета, без отсылки к конкретным задачам проекта.

Обсуждая проект, команда предлагала более подходящие решения, поэтому мы организовали несколько one-to-one сессий для обсуждения мотивов и побуждений тимлида. В итоге он получил ценный опыт по анализу требований и выбрал правильную технологию с наставником. С помощью разных подходов остановил свой выбор на совсем другой базе данных — MySQL с табличным представлением данных, так как она давала дополнительные организационные преимущества. При использовании со Spring Data JPA стало возможным получать подсказки о несовместимости на этапах сборки и тестирования проекта, даже если разработчик только пришел в команду или не сильно знаком с выбранными технологиями. Поскольку проект был небольшой и ресурсов было выделено немного, решили, что управление качеством и ошибками важнее, чем масштабирование.

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

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

Несоответствие шаблона проектирования требованиям

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

Изначальная задача заключалась в разработке приложения под небольшую клиентскую базу — до 10 000 пользователей. Решили создать его с микросервисной архитектурой. Так как масштаб был невелик, команда использовала с множеством сервисов всего две базы данных: SQL Server для отчетности и итоговых таблиц, доступных нескольким сервисам, и MongoDB для работы с исходными данными, поступающими в большом объеме.

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

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

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

Как выяснилось, система использовала обе базы данных параллельно, где SQL Server однозначно требовал большего внимания к performance tuning & optimization, а MongoDB вообще не чувствовал проблем, даже можно было урезать количество ресурсов, выделенных на него.

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

Сами микросервисы требовали частичного переделывания, но это было нестрашно. А вот процессы тестирования, сбор метрик и автоматические алерты настроили почти с нуля. Так, 80% проблем решили с минимальными изменениями в коде, оставшиеся 20% распределили на последующие спринты и решали параллельно с внедрением новых фич.

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

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

Виталий Корж, Java Developer в Luxoft

Event driven state machine — лучший способ потеряться в своем решении

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

Исходя из своего опыта, я вывел несколько правил, которых стараюсь придерживаться.

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

Следующее правило: определившись с фреймворком, следует избегать его модификаций. Если без модификаций не обойтись, выберите другой фреймворк.

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

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

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

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

В итоге получаем:

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

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

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

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

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

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

P. S. Облачные платформы позволяют с легкостью решать подобные проблемы.

Владимир Иванченко, Product Owner в Poster

Встраивать интеграцию со сторонними сервисами прямо в ядро продукта

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

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

Poster написан на JS. Мы обложили коллбеками окно чекаута, и все завелось. Но вскоре обнаружили несколько проблем в таком подходе.

Зависимость от другой компании

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

Лишний код

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

Неспособность масштабироваться

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

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

Сейчас iFrame с интеграцией мы подгружаем только тогда, когда клиент подключил ее. Лишний раз не нагружаем кассу.

Мы полностью выпилили из ядра сторонний код и постепенно передали его InCust на поддержку. Дальше партнеры смогли самостоятельно развивать интеграцию.

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

Андрей Губский, Software Architect, video intelligence AG

К чему может привести смешивание слоев приложения и неправильно настроенная ORM

В далёком 2008 году, когда моя карьера только начиналась, случилась забавная ситуация на одном из проектов. Ребята из соседней команды разрабатывали системы для компании, которая продавала подержанные автомобили в США. Одной из частей этой системы был интернет-каталог машин, где о любой модели можно было узнать множество деталей.

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

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

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

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

Как решали проблему? На уровне ORM добавили настройки, позволяющие более гибко настраивать автоматическое заполнение полей объекта. Сегодня же практически любая современная ORM имеет подобные настройки из коробки. Главное — не забывать ими пользоваться.

Эта и подобные ей проблемы часто возникают при неправильном проектировании приложения, когда смешиваются слой бизнес-логики и слой доступа к данным. Современные ORM сильно располагают к этому, особенно если технология предоставляет инструменты кодогенерации (как, например, Entity Framework). Однако всегда стоит четко понимать, где находится абстракция для реализации бизнес-логики, а где реализация сохранения и выгрузки данных. Разбиение системы на разные слои и раздельное применение классов BLL и DAL позволяет более четко и ясно понимать, где и какие данные система использует.

В целом разделение систем на слои DAL (Data Access Layer) и BLL (Business Logic Layer) уже стало одним из классических подходов при проектировании программных систем.

Преимущества, которые дает разделение:

  1. Слой доступа данных относительно легко изменить, не затрагивая бизнес-логику. Такое может потребоваться при смене СУБД (миграция с одной СУБД на другую) или при введении дополнительных СУБД (например, если к реляционной СУБД в какой-то момент необходимо добавить документоориентированную СУБД и если бизнес-сущности будут «собираться» из нескольких источников).
  2. Тестирование бизнес-логики становится гораздо проще.
  3. Упрощается повторное использование кода, например, когда единые правила валидации каких-либо сущностей могут быть применены в различных проектах одной компании или в различных продуктах.

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

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

Про сайд-эффекты и то, как их предотвратить

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

Сам по себе сервис был простым — он преобразовывал один объект во второй. Поэтому этот сервис даже не стали покрывать тестами (что стоило бы сделать, и это было первой ошибкой). Однако по какой-то причине в него была внесена всего одна строчка кода, которая и привела в дальнейшем к целому часу почёсывания затылка и изучения истории коммитов. Выглядело это примерно так:

public async Task<SomeType> CreateSomeType(Request request)
{
	if (SomeCheck(request))
	{
		request.Url = url.GetDomainUrl();
	}
	
	return new SomeType
	{
		Id = request.Id,
		...
		Url = request.Url
	};
}

Нетрудно догадаться, что строка request.Url = url.GetDomainUrl(); и стала причиной будущих проблем. Тут мы видим сразу две логические проблемы. Первая — изменение уже сформированного объекта запроса. Вторая — изменение объекта, который пришел в качестве параметра. Не делайте так.

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

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

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

  1. Уменьшение вероятности появления сайд-эффектов в системе — иммутабельный объект не может быть «случайно» изменен на каком-то этапе своего жизненного цикла.
  2. Код становится более простым для понимания: уже ознакомившись с сигнатурой класса, новый разработчик, который пока еще не знаком с проектом, сразу будет понимать имеющиеся ограничения и особенности работы с объектами этого класса.
  3. Иммутабельные объекты и коллекции потокобезопасны: если объект или коллекцию нельзя изменить, то и проблем с синхронизацией между различными потоками у вас не будет.

Почему это важно именно для крупных проектов? Когда проект небольшой и над ним работает всего несколько человек, гораздо легче согласовывать изменения. Коммуникация не составляет проблем. Но если проект имеет долгую историю развития, состав команды меняется часто или количество участников команды постоянно растет, то нужны уже ограничения на уровне архитектуры самого приложения, которые помогут избежать непредусмотренных изменений объектов и сайд-эффектов в процессе обработки запросов. Стоит отметить, что в C# 9.0 был добавлен новый тип record, благодаря которому можно реализовывать требования неизменяемости более элегантно.

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

👍НравитсяПонравилось12
В избранноеВ избранном11
Подписаться на автора
LinkedIn

Похожие статьи




Підписуйтесь: SoundCloud | Google Podcast | YouTube


39 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Ох, как наши программисты даже при существующих, хорошо известных best practices — умудряются наделать дел. Про конечные автоматы — надо знать, где их применять, хорошо прописать стейты и переход между ними. Это отличный патерн для game development — есть и другие area, где его можно с успехом применять. Про выбор BD — если из условий транзакционная природа, тут думать нечего, тут однозначно SQL базы данных. В общем, с чем действительно согласен — смотреть на рекваременты и уже, исходя из них выбирать технологии. Плюс, конечно, исходя так же из того, какая инфраструктура есть подходящая, на которой кастомер хотел бы развернуть решение.
Как выразился один Java блоггер — сейчас больше сконцентрированы на компоновке всяких фреймворков, нежели на действительно сложных инженерных задачах. И как он продолжил — не нужно из-за этого надувать щеки :)

Гарна стаття в тому контексті, як гарний приклад що багато людей, які вважають себе знавцями теми «Архітектура ПЗ» на геть не розуміють базових принципів архітектури ПЗ.

Усім авторам В ОБОВ’ЯЗКОВОМУ порядку з’їздити на Почайну на книжковий ринок та купити собі книгу Робіна Мартина «Чиста архітектура» (яка є рідною, українською мовою з досить гарним перекладом), перечитати її у паперовому варіанті де-кілька разів і поставити біля свого робочого місця, щоб періодично підглядати...

Жодних з наведених варіантів не є прикладом внятного рішення по правильній архітектурі ПЗ.

Більш того, є повне нерозуміння і патернів програмування, архітектурних патернів та базових принципів дизайну.
Кожен патерн чітко вирішує певну, наявну, проблему і коли саме ця проблема відсутня, то патерн автоматично стає антипатерном. І таке є так само в усіх наведених «рішеннях»...

Не буду переказувати правильних книжок та статтей грантів по цим темам (таким як Мартін Фовлер, Робін Мартин, «банди чотирьох» та інших), наведу лише де-які ну дуже велики ляпаси які є вочевидь...

Правильна архітектура ПЗ — це архітектура, котра не залежить від рішення по БД.
КРАПКА.
Вже маючи «абстрактний рівень роботи з данними», виходячи від бізнес вимог, CAP-теореми чи чого там ще — робите висновок з якою БД чи набіром сервісів + БД ви маєте справу...

Microservices — це паттерн. Є проблеми,котрі цей паттерн вирішує і мільйон варіантів, котрі роблять цей патерн антіпатерном. Більш того, цей паттерн має ще більш ніж 25 патернів, котрі включають до його складу... І саме наявність певних проблем вимагає використання цих патернів, а не навпаки...

Що таке

Event driven state machine

???
Є архітектурний паттерн «event sourcing», є проблеми, котрі обумовлюють використанян саме цього паттерну, а не, наприклад DDD чи CQRS. І є обов’язкова необхідна умова його використання — наявність unmuttable events... ...доречі, в наведеному прикладі чітко треба застосовувати CQRS.

Є паттерн програмування «reactive programming» AKA «event drive developing» AKA «message drive developing». Чому використовується?

Є паттерн конструювання «state machine».... Чому використовується?

І жодних нотатків чму в проді воно вже MVP...

...і т.д.

Правильна архітектура ПЗ — це архітектура, котра не залежить від рішення по БД.
КРАПКА.

Чувак, загнався сам в свою пастку.

Кожен патерн чітко вирішує певну, наявну, проблему і коли саме ця проблема відсутня, то патерн автоматично стає антипатерном. І таке є так само в усіх наведених „рішеннях”...

Гексагоналка (Ports and Adapters) та енкапсуляція бази даних — такі самі патерни. І мають плюси та мінуси. Мінус — швидкість розробки та швидкість роботи. Плюс — можливість розвитку проекту та незалежність від вендорів.

Дивимось The Smart UI в DDD:

A project needs to deliver simple functionality, dominated by data entry and display, with few business rules. Staff is not composed of advanced object modelers.

If an unsophisticated team with a simple project decides to try a MODEL-DRIVEN DESIGN with LAYERED ARCHITECTURE, it will face a difficult learning curve. Team members will have to master complex new technologies and stumble through the process of learning object modeling (which is challenging, even with the help of this book!). The overhead of managing infrastructure and layers makes very simple tasks take longer. Simple projects come with short time lines and modest expectations. Long before the team completes the assigned task, much less demonstrates the exciting possibilities of its approach, the project will have been canceled.

Even if the team is given more time, the team members are likely to fail to master the techniques without expert help. And in the end, if they do surmount these challenges, they will have produced a simple system. Rich capabilities were never requested.

A more experienced team would not face the same trade-offs. Seasoned developers could flatten the learning curve and compress the time needed to manage the layers. Domain-driven design pays off best for ambitious projects, and it does require strong skills. Not all projects are ambitious. Not all project teams can muster those skills.

Therefore, when circumstances warrant:

Put all the business logic into the user interface. Chop the application into small functions and implement them as separate user interfaces, embedding the business rules into them. Use a relational database as a shared repository of the data. Use the most automated UI building and visual programming tools available.

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

Event driven state machine — лучший способ потеряться в своем решении

Чесно кажучи, так і не зрозумів чому. Загубитися можна в будь-якому рішенні.

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

и в случае надобности выносите функционал в отдельную реализацию.

Що це означає ?

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

Питання не розкрите. З наведеної аргументації схоже, що «преференції» однії людини поміняли на «преференції» іншої.

количество активных пользователей выросло в 10 раз. Тут и начались сложности.

Ще не було системи, яка б таке пережила без доробки.

Метрики не збирали — бо не треба було і за це не доплатили ... та й не знали що збирати.

Проблеми з SQL не проявляють себе на малих даних. Доналаштування БД при зміні об’єму даних — стандартна процедура. Щоб передбачити ріст в 10 разів, треба було нагенерити в базу в 10 разів більше даних на тестах. (За тестування під навантаженням, скоріш теж не заплатили, того і не зробили).

Я вам більше скажу, зросте кількість даних ще в 10-100-1000 разів, база, майже напевно, знову підведе.

А также точно оценить задачу, учитывать перспективу развития проекта через 2, 5, 10 лет.

Це неможливо. Щоб проект жив 2-5-10 років і витримував зміни, потрібна компнда розробників в штат (або на підхваті в аутсорсі).

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

Проблема з ОРМ часто полягає в тому, що розробники ОRМ вирішують додати туди зайвого функціоналу, який чомусь люди вважають, що необхідно використовувати.

Основна задача ORM — це mapping: ResultSet на об’єкти, об’єкти на інсерти, ще мож щось з процедурами.

Query Builder — це вже не задача ОRМ, хоч і для специфічних задач ще згодиться.

А ось join-и, витягування дочірніх об’єктів, генерація запитів, hibernate-івські сесії — це все те, чого варто уникати — ці всі інструменти створюють більше проблем, ніж вирішують.

Базу даних треба поважати: писати складний SQL руками.

Не забывайте грамотно разбивать свою систему на слои.

Шари — це добре. Але в більшості випадків, це є сенс робити на етапі рефакторингу, коли стає очевидним де і що є сенс розділяти. Інакше ж, створиться мінімум по три об’єкти на кожну сутність (dao, dto і, власне, модель) і купа перетворень між ними. В результаті, вагон класів і каша в коді.

Шари — це добре, але Keep It Simple.

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

Звучит, как перекладывание ответственности на клиента

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

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

бюджеты не всегда с этим согласны

В тексте сказано «выбранная архитектура». Значит за нее заплатили

Или сколько заплатили так и получили. Нужен контекст чтобы кейс разбирать.

Если вы шкаф покупаете, а у него дверца отваливается когда вы ее открыть пытаетесь, это «сколько заплатили, столько получили», или это брак?

Заказали комод, а хотели шкаф. Или мы всегда должны шкафы делать?

Нет. Архитектура это «способность эволюционировать». Если первая-же проверка на способность эволюционировать проваливается, значит архитектура бракованная

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

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

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

даааа, вот именно поэтому каждый проект стартуют на микросервисах, дааа

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

Микросервис — это паттерн. Чтобы его использовать --- минимум! — нужно иметь проблему, которую решит его использование. Какую проблему — в вашем случае — этот паттерн должен был решить? И какие еще из более чем 25-ти микросервайс подпаттернов Вы использовали?

...и, собственно, архитектура там где? :)

Спасибо авторам, хорошая полезная статья.

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

Изначальная задача заключалась в разработке приложения под небольшую клиентскую базу — до 10 000 пользователей.

Ну так а в чем тут несоответствие решения? Клиент хотел 10000, получил 10000. Если у него стало 100.000 то это не архитектура плохая, а требования некорректные.

Шутки про архитекторов вошли в чат)

Если проект делается с нуля, то формируется список рисков для mvp,prod,prod* под бизнес-цели
На их основе выбираются оптимальные технологии, которые быстрее всего интегрируются для mvp
Затем mvp отдается заказчику, после чего получает ряд замечаний и у вас повторная итерация, но при этом уже не с нуля (см ниже).

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

небольшую клиентскую базу — до 10 000 пользователей. Решили создать его с микросервисной архитектурой.

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

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

Кто бы мог подумать! Никогда такого не было и вот опять! :)

А вообще хорошая история.

Почему мне смешно? 🤣 Хотя и грустно

Потому что этот комментарий смешной 🤣 Хотя и грустный

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

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

Не бывает плохих или хороших технологий.

Погані — точно бувають.

Сортування бульбашкою :)

А якщо серйозно, то я тут вже написав абзац прикладів в стилі «технологія Х замінила старішу технологію Y», а потім стер — бо і так ясно, що новіша технологія краща тому, що вона новіша. Тут потрібні приклади технологій, що виникли приблизно одночасно і одна з них була сильно гірша за іншу. Ну, наприклад, MFC гірший за Qt. Chakra гірший за V8.
Окрім того, можна навести приклади однозначно поганих технологій з точки зору ліцензування, ціни, підтримки виробником, тощо.

Тут я не зовсім з вами згоден.
Коли технологія створюється, вона не може бути відразу поганою, адже її створювали для вирішення якихось завдань.
Так, потім вона може застаріти, але знову ж таки я не вважаю, що через це вона стане «поганою», просто вона не здатна вирішувати нове коло завдань або не відповідає сучасним вимогам.

Коли технологія створюється, вона не може бути відразу поганою

Чому? Технологію можуть створювати погані розробники. Технологія може бути застарілою відразу зі старту. Технологію можуть створювати з метою розпилу бюджету. Може бути просто невірно оцінена необхідність створення технології. В ідеальному світі, де всі розумні і всім керує логіка, новостворена технологія не може бути поганою, але ми живемо не в такому світі.

Коли технологія створюється, вона не може бути відразу поганою

Кхм, гхм, майкрософт, фатальный недостаток

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

Подписаться на комментарии