Поговорим о том, что такое enterprise разработка, какие у enterprise проектов могут быть нюансы, и какие навыки нужно обрести для успешной работы в разрезе .NET стека.
Приветствую! Меня зовут Андрей Степанов, я CTO во fuse8. Мне интересно знакомиться с опытом коллег по цеху и делиться своим. В сфере я уже больше 20 лет. В этой статье – наблюдения и рекомендации для интересующихся и тех, кто еще не трогал enterprise, но очень хотел бы.
Предположим, вы изучили язык, фреймворк, инструменты, получили опыт разработки и теперь стоите на распутье:
Путь 1 enterprise разработка: микросервисная архитектура, СУБД, очереди, gRPC и проч.
Путь 2 геймдев: Unity.
Путь 3 CMS (content management system) и DXP (digital experience platform): Kentico, Sitecore, Optimizely.
Предлагаем подробно разобраться в нюансах и узнать, к чему нужно готовиться, если хочется в enterprise.
Что такое enterprise
Приходит бизнес-заказчик и говорит: «Сделайте мне продукт!». Заказчику нужно изобрести «собственный велосипед» для выполнения конкретной бизнес-задачи. Такой «велосипед» – это цельная система, и у нее есть особенности при разработке:
Написание кода, который будет поддерживаться многие годы (ООП, SOLID, GOF-паттерны, Unit-тесты и т.д.);
Сложная бизнес-логика, которая часто меняется;
Использование проверенных фреймворков/библиотек;
Написание тестов;
Код, доступный для рефакторинга, и создания документации по продукту на его основе;
Регулярность code review.
Максимально качественный код нужно писать сразу, используя разные подходы и паттерны, чтобы в дальнейшем можно было расширять проект. Поскольку проект будет решать определенные бизнес-задачи, нужно готовиться к пониманию и реализации сложной бизнес-логики. Она, кстати, по ходу развития проекта обязательно будет меняться и дополняться, поэтому зазубрить на один раз не выйдет. А еще нужно будет погрузиться в доменную область, в которой будет вестись разработка.
Enterprise проект — это всегда вопрос про деньги. Мы не можем использовать непроверенные решения.
Годятся только проверенные годами фреймворки, библиотеки, и инфраструктурные сервисы. Нельзя рисковать бюджетами и бросаться на первый попавшийся, только что вышедший новый сервис, даже если он кажется лучшим решением на планете.
Доработка и развитие проекта неразрывны с постоянным тестированием. Оно дает возможность дорабатывать или рефакторить код на протяжении долгих лет, а еще – смело трогать в дальнейшем старые участки кода, не боясь что-то сломать. Это важно, потому что подход «работает – не трогай» тут не подойдет. Ну и без код-ревью в этой связке никуда. Этот процесс помогает отлавливать несовершенства и баги еще до приемки тестировщиками, а еще – шарить знания о разработанной фиче с командой, которая потом сможет исправлять и дорабатывать фичу вместо тебя.
Из чего состоит типичное энтерпрайз приложение
Вот у нас есть центральное приложение, и нам, конечно же, для него нужна база данных. Если пишем проект с нуля, то сразу выясняем, какая база данных подойдет лучше, исходя из задач проекта.
Логирование. Недостаточно просто настроить его, нужно подобрать сервис, который будет сохранять логи. Без логирования зачастую невозможно разобраться в проблемах на проде, невозможно восстановить какие-то артефакты.
Метрики и хелсчеки. Мы не можем, запустив проект, отправить уже созданную его часть в свободное плавание, пока сидим и разрабатываем следующую. Сервис может и упасть, а мы этого не узнаем. Падения – это всегда финансовые потери для бизнеса, а их быть не должно. Чтобы их избежать, нужно превентивно, на основании бизнес-метрик, понять, что идет не так, и исправить ситуацию.
Стоит запомнить, что о багах и проблемах на проде разработчики должны узнавать быстрее заказчика.
Помимо логирования стоит предусмотреть error tracking или трекинг исключений и сервис, который будет хранить ошибки отдельно. Трекинг-сервис помогает группировать, анализировать и фильтровать ошибки, отслеживать их частоту, предоставляет более детальную информацию, чем логи, и быстро отправляет нотификации об ошибках. Можно интегрировать трекинг ошибок с чатами. А еще можно создать кастомные интеграции, чтобы, например, сервис отправлял сообщения об обнаруженных ошибках на телефоны разработчиков.
Внутри enterprise приложения должно быть настроено эффективное общение между сервисами: задачи должны выполняться своевременно, нагрузка – распределяться равномерно. В этом помогут очередь, шина, или планировщик задач.
Приложение нужно будет подстроить под деплой. Поэтому разработчику нужно знать, как будет развертываться инфраструктура. Например, нужно определиться с контейнеризацией и оркестрацией – выбрать сервисы и правильно их настроить.
На проекте могут быть DevOps-специалисты, но это не значит, что ответственность за процесс деплоя будет лежать только на них.
Если у нас монолит, мы можем кэшировать данные в памяти. Если микросервисы и для каждого отдельного сервиса могут подниматься несколько инстансов – кэшировать в памяти каждого не выйдет. Не понятно как это потом синхронно обновлять. В таких случаях на помощь приходит распределенный кэш: отдельно поднимается сервис, который хранит определенные данные и предоставляет быстрый доступ к ним. Как правило, это key-value хранилище.
Отдельные файловые хранилища. Зачем? За тем, что хранить файлы просто на диске – не удобно. Хранить файлы в базе данных – проблемно. Поэтому зачастую в enterprise проектах используются отдельные файловые хранилища. Это еще один инфраструктурный сервис.
Для задач, связанных с отображением данных, нужно подключить визуалайзер. Так удобнее следить за метриками, смотреть логи, отслеживать хелс-чеки. Мы, например, используем Grafana, чтобы выводить данные и присылать нотификации. Grafana позволяет все собранные данные отобразить так, чтобы было удобно их совокупно анализировать.
Отдельный блок – code style и анализаторы. Когда команда уже большая или среднего размера, каждый может писать код по-своему. Это порой вызывает споры на код-ревью. Поэтому зачастую на enterprise проектах используются анализаторы, которые помогают автоматически находить проблемы, потенциальные ошибки и проверять единый code style.
Какие навыки нужны для успешного решения задач энтерпрайз-проекта
Базы данных:
Таблицы, типы данных, связи, ограничения;
написание запросов, соединений;
индексы;
транзакции;
хранимые процедуры;
представления.
Более продвинутый уровень:
План запроса;
статистика;
нормальные формы;
deadlock;
триггеры;
внутреннее устройство фильтрации и соединения.
Понятное дело, что нам нужно уметь самим создавать таблички, продумывать их архитектуру, понимать все типы связи, ограничений и так далее. Это must-have.
Еще нужно ориентироваться в разных видах индексов: покрывающих, фильтрующих, кластерных, не кластерных, составных. Так, уже на этапе разработки будет проще закладывать индексы, чтобы увеличить перформанс определенных запросов. Другой пример: когда мы видим, что запросы выполняются медленно, можем предложить, используя знания об индексах, один из вариантов решения.
Транзакции. Многие запросы нужно делать в рамках транзакций, чтобы не получились неконсистентные данные в БД.
Что нужно, чтобы продумать архитектуру enterprise приложения
DDD;
SOLID;
GOF-паттерны;
REST;
чистая архитектура (гексагональная архитектура);
CI/CD;
паттерны, используемые в микросервисной архитектуре.
Желательно разбираться в DDD-подходе, чтобы сразу правильно спроектировать приложение и сделать архитектуру, которую в дальнейшем можно будет расширять.
Принципы SOLID с точки зрения архитектуры сильно помогают при разработке.
GOF-паттерны нужны, чтобы писать более расширяемое приложение, более универсальное. Его легко можно будет дорабатывать, что нам очень важно.
REST. Придется делать много интеграций со сторонними сервисами, общение между которыми зачастую реализуется через REST. Еще может понадобиться реализация REST API для приложения, чтобы с ним могли общаться другие сервисы, например spa.
Для дальнейшего расширения и развития приложения также разумно применять знания о чистой или гексагональной архитектуре.
Области знаний C# и .Net Framework для enterprise
Инфраструктура enterprise приложения
Базы данных (MS SQL, PostgreSQL, MongoDB)
Логирование (ELK stack, Grafana Loki)
Отслеживание ошибок (Sentry)
Сбор метрик и хелсчеков (Prometheus)
CI/CD (Docker, Kubernetes, TeamCity, GitLab CI/CD)
Очереди, шина данных, планировщик задач (RabbitMQ, Kafka, Hangfire)
Файловое хранилище (Minio)
Распределённый кэш (Redis)
Отображение метрик, логов (Grafana)
Дополнительно к этому всему неплохо бы знать, как работают разные инструменты для поиска проблем в приложении. Например, анализатор памяти (DotMemory), анализатор перфоманса (DotTrace), анализатор запросов в БД (MS SQL Server Profiler).
Микросервисы – модно, молодёжно?
Может показаться, что микросервисы – это классно и круто, что надо каждое новое приложение писать на микросервисах. На самом деле и здесь есть нюансы.
Чтобы реализовать приложения на микросервисной архитектуре, помимо знаний, описанных выше, нужно дополнительно знать паттерны микросервисного взаимодействия.
Возникает куча проблем с распределенными транзакциями. Хорошо, когда у тебя есть монолит, ты можешь сам код обернуть в транзакции. Но с микросервисами это сделать невозможно, потому что в рамках одной транзакции может быть затронуто несколько микросервисов. В этом случае приходится применять паттерн Saga (Повествование).
Профилирование запросов сразу же усложняется несколько раз. Мы не можем взять и посмотреть все его логи у одного сервиса. Весь запрос, начиная от клиента, нужно собрать – воссоздать его «хождение» по разным сервисам. Для этого существуют специальные инструменты.
Сбор логов усложняется. Опять же, у нас нет одного лога у одного предложения, как в монолите. Нужно собирать логи с разных сервисов. Возможно, их придется стандартизировать для удобства отображения и просмотра.
Появляется сильная зависимость от инфраструктуры: очереди, на которые все завязано, например. Кроме интеграции нашего сервиса со сторонними, еще есть интеграция со своими сервисами. Все может упасть. И все эти возможные случаи нужно обрабатывать в коде .
Микросервисы намного сложнее развертывать. Мы не можем поменять API нашего сервиса и залить на прод – сломаются сервисы, взаимодействующие с нашим. Приходится оставлять поддержку предыдущей версии API для плавного перехода. Когда все остальные сервисы перейдут на новую версию API, можно будет удалить поддержку старой версии.
А что с отладкой на компьютере разработчика? Когда один сервис зависит от другого, мы не можем просто поднять через дебаг свой сервис и отдебажить все что хочется. Придется отдельно развернуть другой сервис, от которого мы зависим, и начать дебажить вместе с ним. Либо можно запилить заглушку в своем коде. Словом, добавляется проблема взаимодействия.
За что любят enterprise разработку
Возможность использовать самый последний стек технологий.
Возможность совершенствовать свой код на протяжении долгого времени.
Полное погружение в доменную область бизнеса.
Работа с разными библиотеками и инфраструктурными сервисами.
Самостоятельное проектирование БД и архитектуры приложения.
Хорошая документация по проекту.
Налаженный процесс полного цикла реализации задач.
Есть потенциальная возможность работать на самом последнем стеке технологий – особенно если вовремя переходить между фреймворками, а не затягивать переход на годы.
С другой стороны, если много микросервисов, и они используют общие библиотеки, то может начаться целая история с обновлением до нового фреймворка всех зависимостей. Зато если у вас средний монолит или несколько сервисов, то переходить между фреймворками достаточно просто.
Просто обновившись, можно оптимизировать использование памяти или увеличить перфоманс. Это удобно.
Вы вольны решать, что будете использовать в проекте: какие именно библиотеки, инфраструктурные сервисы и прочее. Иногда можно их поменять, если совсем не подошло изначально выбранное решение. Получается, что разработчик будет владеть разноплановыми технологиями. Многим как раз нравятся такие проекты за то, что на них можно расширить свой кругозор.
Вы сами проектируете базу данных и архитектуру приложения. Это своего рода вызов. С другой стороны, если спроектировать неправильно, то все тапки в вас.
Зачастую enterprise проекты создаются долгое время. Поэтому невозможно обойтись без хорошей документации. Ведь если команда большая, и есть некоторая текучка сотрудников, то без проектной документации будет невозможно поддерживать и дорабатывать сервис. Поэтому следить за ней обязательно – это и плюс.
Над приложением в enterprise трудится целая команда: разработчики, тестировщики, аналитики, менеджеры, девопс-отдел, отдел баз данных. Они на разных этапах контролируют процессы доведения задачи до продакшна и дальнейшего тестирования, поиска проблем. Когда полный цикл реализации задач налажен, проще будет вводить в команду новых сотрудников.
Но не без трудностей
Горящие баги на проде, жtсткие сроки реализации фич.
Нужно обладать хорошими знаниями по C#, .Net, базам данных.
Сложный выбор подходящей под задачу библиотеки или инфраструктурного сервиса.
Без своевременного рефакторинга приложение превратится в большой ком грязи.
Сложный онбординг нового сотрудника.
Свои «велосипеды».
Горящие баги на проде – правда для всех типов проектов. Пример: нам нужно реализовать фичу к определенному сроку, потому что зависим от регуляторных рисков. Тут либо внедряем функционал в срок, либо мы теряем деньги. Когда что-то сломалось на проде, бывает необходимо подключаться в любое время: утром, днем и ночью, чтобы срочно что-то пофиксить.
По-хорошему, чтобы попасть в энтерпрайс команду, нужно обладать хорошими знаниями по базам данных, фреймворку .Net, языку C#, чтобы не совершать элементарных ошибок. Если допускать ошибки в каких-то базовых вещах – неправильно использовать коллекции, асинхронный код – будут блокеры в разработке и расход бюджета впустую.
Необходимость подбора библиотеки или инфраструктурного сервиса – это и плюс, и минус одновременно. С одной стороны, у вас развязаны руки: вы можете попробовать то, что сами хотите. С другой – если вы выберете что-то не то, потом это будет сложно заменить. Нужно уметь думать превентивно.
Вся ответственность за неправильно выбранный сервис или библиотеку лежит на вас. Это и минус.
Рефакторинг. Нельзя написать проект и потом его только дописывать, не возвращаясь к уже реализованным частям. Постоянно придется рефакторить отдельные участки кода или переписывать их по разным причинам. Требования к продукту по мере его развития могут меняться. Поэтому если не будет своевременного рефакторинга, через несколько лет придется просто выбросить это приложение и написать его с нуля. А это очень сложная задача, когда у тебя есть текущее работающее приложение на проде.
У каждой компании есть свой велосипед. Это значит, что разные разработчики используют свои библиотеки, свои инфраструктурные решения, работают в своей доменной области. Поэтому нельзя просто взять и сменить компанию: прийти и сразу же влиться в работу после совершенно других проектов.
На то, чтобы вкатываться в новые проекты, всегда будут требоваться время и усилия.
Что почитать по теме
CLR via C#. Программирование на платформе Microsoft .NET Framework 4.5
Джеффри Рихтер
Паттерны объектно-ориентированного проектирования
Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссиденес
Чистая архитектура. Искусство разработки программного обеспечения
Роберт Мартин
Принципы юнит-тестирования
Владимир Хориков
Микросервисы. Паттерны разработки и рефакторинга
Крис Ричардсон
Реализация методов предметно-ориентированного проектирования
Вон Вернон
За содействие в подготовке материала отдельная благодарность ведущему.NET разработчику Федору Киселеву.