Стек — Java, GWT, Mysql

Опыт — 11 лет

Должность — Старший разработчик в Startup

Обучение некоторых новобранцев

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

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

Проблема возникла, когда мне нужно было объяснить, как был написан код и почему он был написан именно так. Мы использовали GWT, и для тех, кто не знает, это позволило нам написать клиентский код как JAVA и использовать те же сущности, что и с БД.

Сказать, что это было полезно, было бы преуменьшением, но, как и у всего в жизни, у этого были свои недостатки. А именно все, что вы сохраняете в БД, отправляется клиенту. Это могло вызвать и вызвало множество проблем с производительностью, и, оглядываясь назад, я почти уверен, что из-за такого поведения были проблемы с безопасностью. Этот код не был правильно наслоен, но я еще не совсем это знал.

Код, который заставил меня посмотреть и понять слои

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

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

БД

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

Сервер

Его основная задача — обрабатывать вещи, принимать запросы от клиента, выполнять некоторую логику и сохранять их в БД. Это человек посередине, между клиентом и БД.

Клиент

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

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

[API] ← RO (объект представления)→ [Service] ← Entity/RO → [Handler] ← Entity → [Dao/DB]

Клиент выполняет запросы к серверу, это десериализуется в RO.

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

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

Они, в свою очередь, могут использовать DAO (объект доступа к данным) для сохранения и извлечения данных из БД.

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

API

Это фасад, обращенный к клиенту. Не все данные необходимо передавать, чтобы выполнить запрошенное действие. Например, в объекте могут быть неизменяемые поля, и у клиента нет причин отправлять их при обновлении объекта. Или на сервере могут быть поля, которые необходимы только для логики сервера, и нет причин отправлять их обратно через API. Вот почему этот уровень работает с RO, также известными как объекты представления. У них может быть меньше полей или агрегированных полей, которые может использовать клиент.

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

Сервисный уровень

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

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

Обработчики

Обработчики имеют очень четко определенные обязанности, они обычно работают только с одним объектом, и именно они могут выполнять транзакционные действия. Они также единственные, кто общается с DAO/DB.

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

ДАО/БД

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

Пример:

Возьмем, к примеру, процесс публикации новой версии поста. Этот процесс запускается запросом от клиента, все, что нужно, это post_id и version_number. Итак, у нас есть RO только с этими полями.

Процесс начинается с некоторой проверки

Этот post_id существует?

Может ли пользователь, запрашивающий публикацию, выполнить это действие?

Есть ли такой номер версии?

Он уже опубликован?

Можно еще что-нибудь придумать....

Теперь, конечно, в конце этого мы хотим транзакционно обновить флаг is_published одновременно в старой версии и в новой. Но пока мы проводим проверки, нет причин открывать транзакцию записи в БД и блокировать объекты. Гораздо лучше выполнить процесс проверки, вызвать обработчик только для выполнения обмена, а затем продолжить после транзакции, чтобы создать результат для клиента.

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

Кратчайшие сроки сделки.

Не используется подключение к БД, если запрос не выполняется на этапе проверки.

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

Четкие отдельные обязанности для каждого слоя.

Практикуйте то, что я проповедую

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

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

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