Разработка с учетом будущих изменений

Эволюционное проектирование закладывает основу для непрерывных инноваций

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

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

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

Рекомендации

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

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

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

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

Предоставьте открытые интерфейсы. Старайтесь не создавать слои преобразования, размещенные между службами. Каждая служба должна предоставлять API-интерфейс с четко определенным контрактом API. Для API-интерфейса важно поддерживать систему версий, чтобы развивать его с сохранением обратной совместимости. Это позволит вам обновить одну службу, не управляя обновлениями всех вышестоящих служб, которые от нее зависят. Общедоступные службы должны предоставлять интерфейс API RESTful по протоколу HTTP. Для серверных служб допустимо использовать протокол обмена сообщениями в стиле RPC для повышения производительности.

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

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

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

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