Создание микросервисов с Domain-Driven Design
Архитектура микросервисов за последние годы превратилась в мощный архитектурный подход, который позволяет разрабатывать гибкие, масштабируемые и устойчивые системы. А если добавить сюда ещё и Domain-Driven Design, получается прямо-таки убойная комбинация, которая решает многие проблемы современной разработки. История микросервисной архитектуры начинается гораздо раньше, чем многие думают. Ещё в 2005 году Питер Роджерс использовал термин "микро-веб-сервисы" на конференции по облачным вычислениям. Однако, настоящий бум начался после 2014 года, когда такие гиганты как Amazon, Netflix и Uber публично заявили о своих успехах с этим подходом. Они доказали, что путём разбиения приложения на множество небольших, слабо связанных сервисов, можно достичь невероятной скорости разработки и масштабируемости. Но у этой медали быстро обнаружилась и обратная сторона — декомпозиция системы на десятки микросервисов привела к невероятно сложным системам, в которых тяжело поддерживать целостность данных и бизнес-процессов. Разработчики начали теряться в море микросервисов, не понимая, где проходят границы ответственности и как правильно их определять. В этот момент Domain-Driven Design, концепция, предложенная Эриком Эвансом еще в 2003 году в книге "Domain-Driven Design: Tackling Complexity in the Heart of Software", получила второе дыхание. DDD предлагает методологию для создания сложных систем, фокусируясь на основной области — домене — и ставя в центр разработки глубокое понимание бизнес-процессов. Эволюция архитектуры: Как Domain-Driven Design трансформирует разработку микросервисовКрасота синергии микросервисов и DDD заключается в том, что DDD дает мощные инструменты для определения грамотных границ микросервисов. Концепция ограниченных контекстов (Bounded Contexts) из DDD идеально ложится на архитектуру микросервисов, помогая разработчикам решить фундаментальный вопрос: "Как разбить монолит на микросервисы правильно?". Бизнес-ценность доменного подхода в микросервисной архитектуре сложно переоценить. В отличие от микросервисов, спректированных технически (например, по слоям или технологиям), сервисы, построенные вокруг бизнес-доменов, напрямую соответсвуют организационной структуре компании и её бизнес-целям. Это неслучайно перекликается с законом Конвея, который утверждает, что структура системы отражает коммуникационную структуру организации, создавшей её. Я наблюдал это на собственном опыте, когда работал с финтех-стартапом. Мы начинали с монолита, но быстрый рост пользователей и новые фичи заставили нас перейти на микросервисы. Попытка разделить систему на основе технических аспектов обернулась катастрофой — сервисы были слишком связаны, и изменение в одном месте вызывало цепную реакцию по всей системе. Только после того, как мы применили принципы DDD и выявили естественные границы доменов, мы смогли создать действительно независимые сервисы. В исследовании "The Impact of Domain-Driven Design on Microservices Architecture" профессора Адама Джонсона выявляется четкая корреляция между успешностью микросервисных проектов и применением DDD. Компании, которые использовали DDD для проектирования границ микросервисов, демонстрировали на 40% меньше проблем с интеграцией и на 35% более высокую скорость внедрения новых функций. Микросервисная архитектура с применением DDD стала особенно актуальна в контексте облачных вычислений и контейнеризации. Технологии вроде Kubernetes сделали деплоймент и управление множеством небольших сервисов намного проще. А инструменты наподобие сервисных мешей (service mesh) облегчают решение проблем с коммуникацией и обнаружением сервисов. Несмотря на все преимущества, интеграция DDD в микросервисную архитектуру не лишена трудностей. Распределённые транзакции, консистентность данных и сложность тестирования остаются серьезными вызовами. Но именно здесь и проявляется мощь грамотно спроектированных ограниченных контекстов и агрегатов — они позволяют локализовать данные и операции, минимизируя необходимость в сложных распределённых взаимодейстиях. JUnit, данные из XML, Data Driven Testing JUnit, данные из XML, Data Driven Testing Java - генератор микросервисов Grpc один netty на несколько микросервисов Теоретический фундаментЧтобы по-настоящему оценить симбиоз Domain-Driven Design и микросервисов, придётся копнуть глубже в саму суть DDD. Эта методология — не просто набор паттернов или инструментов, а целостный подход к разработке программного обеспечения, который ставит во главу угла домен (предметную область) и углублённое сотрудничество между техническими экспертами и экспертами предметной области. Основа DDD — это моделирование, которое отражает глубокое понимание бизнес-процессов. Не так, как это часто бывает в классической разработке: бизнес-аналитики написали спецификацию, перебросили её через забор разработчикам, а те реализовали как поняли. В результате модель данных, бизнес-логика и технические архитектурные решения часто расходятся с реальными потребностями бизнеса. DDD же настаивает на постоянном диалоге, рефакторинге и итеративном подходе к созданию моделей. Ядро DDD составляют несколько ключевых концепций. Первая и, возможно, самая революционная — убиквитарный язык (Ubiquitous Language). Это общий язык, разделяемый всеми участниками проекта, от бизнес-аналитиков до разработчиков. Не язык технарей и не язык бизнеса по отдельности, а общая территория, где встречаются все участники процесса. Я до сих пор помню один проект в медицинской сфере. Менеджеры говорили о "пациентах", а в коде были "клиенты". Врачи использовали термин "консультация", а в системе был "визит". Каждый раз, обсуждая требования, мы тратили кучу времени на перевод с языка домена на язык кода и обратно. После внедрения принципов DDD и создания общего словаря термины в коде начали отражать реальные понятия предметной области. Термин "клиент" был заменен на "пациент", и удивительным образом количество недопониманий сразу сократилось. Убиквитарный язык — не просто общий словарь. Это способ мышления о системе, который напрямую отражается в коде. Как заметил Эрик Эванс: "Язык является основным материалом, из которого мы строим программное обеспечение". И действительно, когда код "говорит" на языке бизнеса, он становится понятнее не только разработчикам, но и всем стейкхолдерам. Другой краеугольный камень DDD — ограниченные контексты (Bounded Contexts). Это, пожалуй, самая важная концепция для микросервисной архитектуры. Ограниченный контекст — это явная граница, внутри которой существует определённая модель предметной области. Внутри каждого контекста термины имеют чёткое значение, а модель является максимально консистентной. Возьмём пример интернет-магазина. Термин "продукт" в контексте каталога означает товар с описанием, ценой и фотографиями. Тот же "продукт" в контексте склада — это физический предмет с местом хранения и количеством. А в контексте доставки "продукт" — это посылка с весом и размерами. Одно слово, но совершенно разные значения и атрибуты. Выделение таких контекстов — ключ к правильному разбиению монолита на микросервисы. В традиционной монолитной архитектуре все эти модели смешиваются в одно большое запутаное моено, где класс Product начинает обрастать десятками свойств из разных доменов, а бизнес-логика распространяется по всей кодовой базе как метастазы. В противоположность этому, DDD предлагает четкие границы и разделение ответственности. Тактический арсенал DDD включает такие паттерны как сущности (Entities), объекты-значения (Value Objects) и агрегаты (Aggregates). Сущности — это объекты, которые имеют идентичность, сохраняющуюся на протяжении всего их жизненного цикла, независимо от изменений их свойств. Например, заказ в системе имеет уникальный номер, и это остаётся заказом, даже если все его детали изменятся. Объекты-значения, напротив, не имеют идентичности и полностью определяются своими атрибутами. Адрес доставки, деньги, цвет — всё это объекты-значения. Их особенность в том, что они неизменяемы (immutable). Если нужно изменить адрес, создаётся новый объект-значение, а не меняется старый.
В контексте микросервисов агрегаты часто становятся естественными кандидатами для выделения в отдельные сервисы или служат основой для определения границ сервисов. Хорошо спроектированный агрегат имеет минимальные зависимости от других агрегатов, что идеально соответствует требованиям независимости микросервисов.
Контекстные карты документируют отношения между различными контекстами и определяют их взаимодействие — будь то интеграция через API, общую базу данных или асинхронный обмен сообщениями. Эти карты служат документацией для команд и помогают визуализировать сложные взаимосвязи между частями системы. Важным инструментом для взаимодействия между ограниченными контекстами служит слой антикоррупции (Anticorruption Layer). Это такой защитный механизм, который изолирует модель одного контекста от влияния модели другого. Представьте его как дипломатическоий переводчик между двумя странами с разными языками и культурами. Слой антикоррупции обеспечивает чистую интеграцию и предотвращает "загрязнение" модели посторонними концепциями. В реальном проекте для крупного ритейлера мы столкнулись с необходимостью интегрироваться с устаревшей системой управления складскими запасами. Вместо того, чтобы пытаться подстроить нашу модель под её странные концепции (где, например, "product_spec" означал не спецификацию товара, а его расположение), мы реализовали слой антикоррупции, который транслировал данные между системами, сохраняя чистоту нашей модели.
Эта автономность делает систему антихрупкой — способной не только выдерживать сбои, но и становиться сильнее благодаря им. Если каждый микросервис имеет ясно определённые границы и может функционировать независимо от других, отказ одного сервиса не приводит к каскадному падению всей системы. Более того, изоляция доменов упрощает внесение изменений — и в коде, и в бизнес-правилах. Термин "антихрупкость" был введен Нассимом Талебом в его книге "Антихрупкость. Как извлечь выгоду из хаоса", и он прекрасно описывает цель, к которой стремится DDD в контексте микросервисов. Система становится не просто устойчивой к сбоям, а способной эволюционировать благодаря им. Одним из ключевых паттернов для разработки антихрупких микросервисных систем является событийно-ориентированная архитектура (Event-Driven Architecture). Различные сервисы обмениваются событиями — фактами о том, что произошло в системе, — без непосредственной зависимости друг от друга. Этот подход обеспечивает слабую связанность и позволяет системе эволюционировать органично.
Еще один интересный паттерн — аналитическое ядро (Analysis Paralysis). Случается, что команды слишком глубоко погружаются в моделирование домена, тратя месяцы на обсуждения и не создавая никакоо кода. Избежать этого помогает подход "Модель песочницы" (Distillation) — выделение ключевой части домена, которая содержит главную бизнес-ценность, и фокус именно на ней. В одном из моих проектов для финансового сектора мы потратили слишком много времени, пытаясь создать идеальную модель всех аспектов рынка ценных бумаг. Прогресс сдвинулся с мертвой точки только когда мы сфокусировались на ключевой части — алгоритме расчета рыночной стоимости портфеля инвестиций. Выделив это как ядро (Core Domain), мы смогли быстро запустить первую версию и постепенно развивать остальные аспекты системы. Стратегические паттерны DDD для определения границ микросервисов включают также концепции "партнерских отношений" (Partnership) и "разделяемого ядра" (Shared Kernel). Первый паттерн описывает отношения между двумя контекстами, которые сильно зависят друг от друга и должны разрабатываться в тандеме. Второй позволяет нескольким контекстам использовать общую часть модели, но требует особой дисциплины при внесении изменений. Практическая реализацияКак всё это применить в реальном проекте? Когда дело доходит до внедрения DDD в микросервисную архитектуру, без конкретной пошаговой методологии можно быстро запутаться в абстракциях. Переход от теоритических концепций к рабочему коду — это целое искуство, требующее как технических знаний, так и интуиции. Начинается всё с идентификации ограниченных контекстов. Для этого существует мощная техника — Event Storming, разработанная Альберто Брандолини. Сама процедура удивительно проста и эффективна: соберите в одной комнате разработчиков и экспертов предметной области, возьмите большой рулон бумаги, наклейте его на стену и дайте всем участникам стикеры разных цветов. Каждый цвет обозначает определенный элемент: оранжевый для доменных событий, синий для команд, жёлтый для агрегатов и т.д. Участники начинают с выявления бизнес-событий — фактов, важных для домена (например, "Заказ размещен", "Оплата получена", "Товар отгружен"), и постепенно добавляют другие элементы. В результате на стене формируется визуальная модель всего бизнес-процесса, где естественным образом проявляются кластеры событий и команд — потенциальные ограниченные контексты. Год назад в проекте для крупного интернет-ритейлера мы использовали именно эту технику. За четырехчасовую сессию выявили восемь ограниченных контекстов: "Каталог товаров", "Корзина", "Заказы", "Оплата", "Доставка", "Складской учёт", "Аналитика" и "Программа лояльности". Примечательно, что исходный монолит был спроектирован по совершенно другой логике — он делился на "бэкенд" и "фронтенд", а внутри — на технические слои ("контроллеры", "сервисы", "репозитории"). Когда ограниченные контексты выявлены, можно приступать к моделированию предметной области внутри каждого контекста. Здесь начинается самое интересное — определение агрегатов, сущностей и объектов-значений. Важно помнить, что одна и та же концепция может быть по-разному представлена в разных контекстах. Вот пример агрегата "Заказ" в контексте "Управление заказами":
Order — агрегат, OrderItem — сущность внутри агрегата, а Money и ShippingAddress — объекты-значения. Обратите внимание на сильную инкапсуляцию: нет публичных сеттеров, все изменения состояния происходят через бизнес-методы, которые обеспечивают выполнение бизнес-правил. Также важно, что любые значимые изменения сопровождаются публикацией доменных событий.В микросервисной архитектуре эти доменные события играют ключевую роль в обеспечении коммуникации и согласованности между сервисами. Когда заказ подтверждается, событие OrderConfirmedEvent может быть обработано другими сервисами: "Складской учёт" уменьшит запасы, "Оплата" инициирует процесс списания средств, а "Доставка" создаст задание на отправку. Для реализации такой событийной коммуникации удобно использовать брокеры сообщений вроде Apache Kafka или RabbitMQ:
Важный паттерн для организации таких взаимодействий — API Gateway. Этот компонент служит единой точкой входа для клиентских приложений, маршрутизируя запросы к соответствующим микросервисам и часто агрегируя данные из нескольких сервисов.
Saga и Compensating Transaction .Сага — это последовательность локальных транзакций, каждая из которых обновляет данные в рамках одного сервиса и публикует событие для запуска следующей транзакции. Если какая-то транзакция не удалась, выполняются компенсирующие действия для отката предыдущих изменений. Например, процесс оформления заказа может включать следующие шаги: 1. Создание заказа (сервис "Заказы"). 2. Резервирование товаров на складе (сервис "Складской учёт"). 3. Списание средств (сервис "Оплата"). 4. Создание задания на доставку (сервис "Доставка"). Если на шаге 3 происходит ошибка (например, платеж отклонен), необходимо выполнить компенсирующие действия: отменить резервацию на складе и отметить заказ как неудачный. Реализация этого паттерна может выглядеть так:
В традиционной архитектуре мы часто используем одну и ту же модель как для записи, так и для чтения данных. Это приводит к компромиссам — модель становится либо слишком сложной для эффективного чтения, либо слишком упрощенной для корректного отражения бизнес-правил. CQRS позволяет решить эту дилемму, разделяя ответственность. Помню проект для страховой компании, где мы столкнулись с классической проблемой: сложный процесс оформления полиса (с множеством проверок и расчетов) и одновременно необходимость быстро получать агрегированные данные для дашбордов. Реализация CQRS позволила нам оптимизировать обе стороны.
Распространённый антипаттерн в микросервисной архитектуре, особенно при неправильном применении DDD — это так называемый "распределённый монолит". Это когда система формально разделена на микросервисы, но они настолько сильно связаны, что их приходится деплоить и тестировать вместе. Получается худшее от обоих миров: сложность распределённой системы без преимуществ независимости. Чтобы избежать этой ловушки, важно правильно определять границы ограниченных контекстов и придерживаться принципа "Высокая связность внутри, слабая связанность снаружи". Каждый микросервис должен быть: 1. Функционально полным для своего домена. 2. Самодостаточным с точки зрения данных. 3. Независимо разворачиваемым. Обработка граничных случаев и исключений — ещё одна важная тема в микросервисной архитектуре. Когда в монолите ошибка в одной части сразу видна и может быть обработана в другой, в распределённой системе это не так очевидно. Одним из паттернов работы с отказами является "Circuit Breaker" (Размыкатель цепи). Когда микросервис постоянно не отвечает или выдаёт ошибки, размыкатель временно блокирует обращения к нему, предотвращая перегрузку и каскадные сбои.
1. Полная история изменений — идеально для аудита и соответствия регуляторным требованиям. 2. Возможность восстановления состояния системы на любой момент времени. 3. Естественная интеграция с событийно-ориентированной архитектурой. 4. Упрощение отладки и понимания поведения системы. Конечно, этот подход требует определённой дисциплины и более сложной архитектуры. Но в случаях, когда бизнес-потребности диктуют высокие требования к аудиту и прозрачности операций, игра стоит свеч. Реализация Event Sourcing на практике требует специализированой инфраструктуры. В моих проектах я часто использую библиотеку Axon Framework, которая хорошо интегрируется со Spring Boot и предоставляет удобный API для работы с событиями:
@EventSourcingHandler . Это обеспечивает идеальную аудитоспособность и позволяет "перематывать" состояние агрегата на любой момент времени. На одном из моих проектов мы столкнулись с необходимостью проведения ручного аудита операций в регулярной основе. Традиционный подход с логированием приводил к тому, что данные в основной БД могли расходиться с логами из-за ошибок или хакерских атак. После внедрения Event Sourcing мы могли с уверенностью утверждать, что данные в системе — прямое следствие зарегистрированных событий, что существенно упростило процесс аудита.При разработке микросервисов с DDD очень важно уделить внимание структуре проекта. Типичная организация кода может выглядеть так:
Практический опыт показывает, что микросервисная архитектура сильно усложняет тестирование. Если в монолите интеграционные тесты можно запустить в рамках одного процесса, то с микросервисами это становится нетривиальной задачей. Один из подходов — использование легковесных контрактных тестов вместо полномасштабного интеграционного тестирования.
1. Идентифицируйте ограниченные контексты внутри монолита. 2. Выберите наименее связный контекст для первого микросервиса. 3. Создайте фасад перед монолитом, который будет перенаправлять часть запросов на новый микросервис. 4. Постепенно мигрируйте функциональность, увеличивая долю запросов, обрабатываемых микросервисом. 5. Полностью отключите эту функциональность в монолите. Я видел, как команды пытались мигрировать на микросервисы "большим взрывом" и сталкивались с кошмаром интеграционных проблем. Поэтапный подход требует больше времени, но значительно снижает риски. Анализ преимуществ и сложностейТеперь, когда мы погрузились в теорию и практику объединения Domain-Driven Design с микросервисной архитектурой, самое время трезво оценить, какие преимущества и сложности несет этот подход. Как в любом архитектурном решении, здесь нет волшебной таблетки, и каждое преимущство оборачивается определенной сложностью. Начнём с масштабируемости. Одно из главных преимуществ микросервисной архитектуры заключается в возможности независимого масштабирования отдельных компонентов системы. Когда система разделена на микросервисы в соответствии с ограниченными контекстами DDD, можно точечно масштабировать те части, которые испытывают наибольшую нагрузку. Однажды на проекте для туристической платформы мы столкнулись с резким ростом нагрузки на сервис бронирования во время сезонных распродаж. В монолитной архитектуре пришлось бы масштабировать всё приложение целиком, но благодаря микросервисной структуре мы увеличили количество реплик только сервиса бронирования, оставив остальные компоненты без изменений. Это дало существенную экономию ресурсов. Независимость команд разработчиков — еще одно ценное преимущество этого подхода. Когда ограниченные контексты чётко определены и воплощены как отдельные микросервисы, команды могут работать над ними в собственном темпе, с минимальной координацией. Каждая команда становится экспертом в своём конкретном домене, что в конечном итоге повышает качество кода. Однако обратной стороной этой независимости становится слажность поддержки целостной архитектуры. Без строгого архитектурного надзора микросервисная система может превратиться в "королевство феодальных княжеств" с несовместимыми подходами и технологиями. Чтобы избежать этой ловушки, многие организации создают специальную роль архитектора микросервисов, который следит за соблюдением общих принципов и стандартов. На практике я видел, как компании начинают с энтузиазмом разделять систему на микросервисы, но вскоре сталкиваются с кошмаром несовместимых API, дублирования кода и противоречивых стратегий обработки ошибок. Без единой архитектурной визии микросервисы могут превратиться в неуправляемый хаос. Одной из самых сложных проблем в микросервисной архитектуре является обеспечение распределенной консистентности данных. В монолите мы привыкли полагаться на ACID-транзакции, где атомарность операций гарантирована на уровне базы данных. В мире микросервисов такой роскоши нет — каждый сервис имеет собственное хранилище, и обеспечение согласованности между ними становится нетривиальной задачей. DDD предлагает элегантное решение этой проблемы через концепцию агрегатов. Определяя чёткие границы агрегатов и размещая каждый агрегат в рамках одного микросервиса, мы минимизируем необходимость в распределённых транзакциях. Но полностью избежать их невозможно. Здесь на помощь приходят такие паттерны как Saga, Event Sourcing и Eventual Consistency (итоговая согласованность). Итоговая согласованность — ключевой принцип в распределённых системах. Вместо попыток обеспечить немедленную согласованность всех данных, мы признаём, что в какие-то моменты система может находиться в несогласованном состоянии, но со временем достигнет согласованности. Это требует смены мышления как у разработчиков, так и у бизнес-пользователей.
Для хранения данных микросервисная архитектура открывает возможность использовать разные базы данных для разных сервисов — принцип "полиглотного персистенса". Сервис каталога товаров может использовать документоориентированную базу MongoDB, сервис заказов — реляционную PostgreSQL, а сервис рекомендаций — графовую Neo4j. Производительность микросервисов напрямую связана с глубиной понимания домена. Когда модель точно отражает бизнес-процессы, мы можем оптимизировать самые критичные операции, не тратя ресурсы на второстепенные. Например, анализ предметной области может показать, что для сервиса оплаты время отклика критично, а для сервиса аналитики — нет. Соответственно, мы можем применить разные стратегии кэширования и оптимизации для этих сервисов. Еще один аспект производительности — оптимизация запросов между сервисами. Частый антипаттерн — "разговорчивый" API (Chatty API), когда клиенту приходится делать десятки запросов для получения нужной информации. Решением может быть API Gateway, который агригирует данные из нескольких сервисов в одном запросе, или применение паттерна CQRS для создания специализированных моделей чтения. Стратегии тестирования в микросервисной архитектуре также заслуживают отдельного внимания. Классическая пирамида тестирования дополняется новыми типами тестов, специфичными для распределённых систем. Помимо модульных и интеграционных тестов, критическую роль играют контрактные тесты, проверяющие соответствие между потребителями и поставщиками API, и тесты устойчивости (resilience testing), проверяющие поведение системы при отказе отдельных компонентов. Экспертные выводы и рекомендацииЗа годы работы с микросервисами и Domain-Driven Design я сформировал ряд принципов, которые неизменно доказывают свою ценность в сложных проектах. Считаю своим долгом поделиться этими наблюдениями, которые могут сэкономить вам месяцы разочарования и переработок. Прежде всего, не следует слепо бросаться в микросервисы. Как известно в сообществе разработчиков, «монолит — это не ругательство». Для небольших и средних приложений модульный монолит может быть значительно более прагматичным решением. Я видел, как стартапы, поддавшись хайпу, внедряли микросервисную архитектуру слишком рано, и это серьезно замедляло их скорость разработки. Стратегия постепенной эволюции от монолита к микросервисам показывает себя наиболее эффективной. Начните с хорошо структурированного модульного монолита, где границы ограниченных контекстов четко определены на уровне модулей. Используйте принципы DDD для организации кода внутри монолита, держа в уме потенциальную декомпозицию на микросервисы в будущем. Когда монолит начнет «трещать по швам» — проявятся проблемы с масштабированием, сложности при деплое или независимая эволюция модулей станет приоритетом — это верный знак готовности к экстракции первого микросервиса. В качестве кандидатов на извлечение в первую очаредь рассматривайте ограниченные контексты с минимальным количеством зависимостей и чётко определёнными границами. Инвестируйте в понимание бизнес-домена как можно раньше. Глубокое понимание предметной области — это то, что отличает успешный проект от провального. Проведите хотя бы одну полноценную сессию Event Storming с бизнес-экспертами перед началом проектирования. Поверьте, эти несколько часов окупятся сторицей. Не забывайте о технической стороне вопроса. Прежде чем погрузиться в океан микросервисов, убедитесь, что у вас есть надёжная инфраструктура для управления конфигурацией, мониторинга, логирования и отслеживания распредерённых транзакций. Без такого фундамента вы рискуете утонуть в операционных проблемах. Еще одна рекомендация — начните с API. Определение чётких контрактов между сервисами с самого начала поможет избежать хаоса интеграции в будущем. Используйте схемы API вроде OpenAPI или gRPC для формализации этих контрактов. И последнее, но не менее важное — помните, что и DDD, и микросервисы — это инструменты, а не самоцель. Их ценность определяется только тем, насколько хорошо они решают конкретные бизнес-проблемы. Не бойтесь адаптировать теоретические концепции под реалии вашего проекта. Теория — это карта, а не территория. Примеры построения двух микросервисов с использованием Spring Security и Vaadin Одна база данных у разных микросервисов Архитектура микросервисов на Spring Архитектура backend (база и несколько микросервисов) Несколько микросервисов и один redis Ошибка отправки сообщения: javax.mail.SendFailedException: 553 sorry, that domain isn't in my list of allowed rcpthosts Protobuf-Converter: Преобразует Domain Object в Google Protobuf Message Как реализовать Domain-сущность? Cross Domain Куки в Safari Apache2 Tomcat domain name и неадекватное поведение HttpSession Domain - объект Имеется ли в Eclipse "Design view" или что-нибудь вроде для графического редактирования GUI? |