Автор: Брат Сяо Фу блог:https://bugstack.cn
Осаждайте, делитесь, растите, чтобы вы и другие могли что-то получить! 😄
Всем привет, я Брат, технический менеджер UP. Фу。MVC объяснил,ДДД объяснил. Следующая глава,Поговорим о реконструкции отMVCприезжатьDDD!
Чтобы избежать путаницы понятий и облегчить объяснение следующего содержания, сначала подтвердите понимание концепции DDD;
Итак, далее в этом контексте мы объясним реконструкцию MVC и DDD;
MVC Старый проект серьезно поврежден, а стоимость итерации слишком высока. ДДД Весь новый проект был реструктурирован, и темп был слишком большим. Это текущий этапсуществоватьпроект Систематическое управлениесередина,Самая большая проблема, с которой мы сталкиваемся;Хотите использовать его DDD Идея состоит в том, чтобы поэтапно реконструировать существующие проекты, не разрушая исходную структуру инженерной системы, чтобы сохранить эффективность принятия новых требований.
На практике мы знаем, что архитектура DDD может решить многие проблемы, возникающие в текущей структуре анемии MVC.
Как мы все знаем, иерархическая структура MVC представляет собой анемичную модель, которая разделяет «состояние» и «поведение» на различные структуры пакетов для разработки и использования. Объекты po, vo и enum написаны в домене, а реализация функциональной логики написана в сервисе. Именно потому, что структура MVC не имеет большого количества ограничений, скорость ранней доставки очень высока. Однако с длительной итерацией системного проектирования анемичные объекты стали использоваться многими службами перекрестно, а сервисные службы также звонили друг другу. Отсутствие метода контекстной разработки приводит к тому, что долгосрочные итерации проектов MVC постепенно приводят к серьезному повреждению.
Коррумпированная основа разработки MVC,«Просто существование» — это перекрестная путаница объектов, сервисов и компонентов. Чем дольше время,Тем более он коррумпирован.
Существуют иерархическая структура MVC, подобная тому, как если бы мы собрали всю одежду в большом гардеробе, а все штаны — в большом шкафу для хранения вещей. Одежда и брюки (предмет),Очень экономит место при редком использовании,потому что другие тоже могут носить твои штаны,Используйте его повторноразвиватьочень быстро。Но со временем,Ситуация становится все более хаотичной. 🤨 Пара брюк становилась все больше и больше, и все их носят.
и DDD Модель многоуровневой архитектуры,Это с человеческой точки зрения,Человек – это поле, а поле – это необходимая ему одежда, брюки, носки и обувь.。Хотя поначалу это была пустая трата места.,Но при длительном цикле разработки программного обеспечения,Последующие расходы на техническое обслуживание будут снижены.
Итак, далее мы сосредоточимся на следующем: как выполнить облегченную реконструкцию из MVC в DDD. 🍻
В конце статьи дается практическое объяснение рефакторинга кода с MVC на DDD. Эта статья также представляет собой практическое объяснение рекомендаций по архитектурному кодированию для MVC и DDD.
Эта статья более практична и может быть реализована DDD Обмен знаниями также начинается с MVC приезжать DDD Объяснение реализуемых решений. существуют Эта статья середина познакомит вас DDD Многоуровневая структура под архитектуру, панорама вызова и что очень важно MVC приезжать DDD Как это должно быть отображено и закодировано. Итак, следующая серия содержания представляет собой знания, которые вы можете получить;
Кроме того, в дополнение к этому фрагментированному изучению знаний, есть также практические упражнения по проектам на уровне приложений; проектирование архитектуры DDD лотереи, проектирование новой архитектуры DDD ChatGPT, проектирование сеанса шлюза API - изучение архитектурных возможностей и мышления в области программирования, а также высокого класса. навыки кодирования.
существовать DDD На уровне архитектуры домен Самый важный модуль одновременно является и самым большим. Все остальные модули вращаются вокруг него. все domian Каждый модуль домена содержит полный набор моделей; - Модельный объект, сервис - Служба обработки, и когда существуют потребности в работе базы данных, вводят соответствующие IRepository - Складские услуги. этот domain Реализация,Это как осознать мешок с динамитом,Взрывной пакет с порохом, вести, Оберточная ткань и другие материалы упаковываются вместе.
Ниже представлена четырехуровневая архитектура, основанная на архитектуре DDD. Она может немного отличаться от некоторых других уровней DDD, но основная структура ключей остается неизменной. В частности, поле предметной области и основа инфраструктуры представляют собой многоуровневые модули, необходимые для любого уровня архитектуры DDD.
Таким образом, это инженерная иерархическая структура модели в соответствии с идеей архитектуры DDD. Ключевые моменты предметно-ориентированного проектирования архитектуры DDD включают более четкие структурные границы, акцент на контекстных вызовах, а также разделение бизнес-функций и базовой поддержки. Словом, каждый выполняет свои обязанности. Итак, учитывая такую четкую структуру проекта, как преобразовать старый проект MVC в DDD? Далее давайте сосредоточимся на этом.
На практике доказано, что это не стоит слишком дорого, MVC может естественно DDD Преобразование структуры модели для инженерных расслоений. Ключевой момент — не менять зависимости исходных инженерных модулей и заменять анемичные domain слой объекта,Создан для насыщенияизструктура。для domain оригинальныйсуществовать MVC слоистыйструктурасередина,Это зависимый слой,Его можно использовать с другими уровнями для реализации решений по инверсии зависимостей.。Как показано на рисунке;
Левая сторона — наша общая MVC Иерархическая структура, правая часть, объяснена вам выше. DDD Иерархическая структура. от MVC приезжать DDD Сопоставления отмечены одинаковым цветом. Некоторые подробности я представлю позже;
существовать MVC Иерархическая структура середина, вся логика интегрирована серединасуществовать. service Этот слой также является самым поврежденным слоем в Венсередина Типриезжать, и именно этот слой необходимо вылечить. Итак, сначала мы должны service Разобрать функции внутри.
Потому что так бывает, что в иерархической структуре MVC сервис и dao также зависят от домена, что соответствует иерархической структуре DDD. Следовательно, после разделения кода посредством такого сопоставления для реализации структуры вызова структура проекта не изменится. Таким образом, пока структура проекта не изменится, наши затраты на трансформацию — это только затраты на стиль кодирования и миграцию старого кода.
MVC в иерархической структуре export слой RPC Уровень определения интерфейса состоит из web реализация слоя. сеть да service вызов. То есть DDD Вызывается в иерархической структуре application Договариватьсяхорошийиз Служить。этотчасть Никаких изменений не требуется。Но если ваш первоначальный проект domain Если он также утек, вам необходимо перенести соответствующий пакет в приезжать. export потому что domain В пакете слишком много основных объектов и свойств, включая объекты сохранения базы данных. Ничего из этого не должно быть раскрыто.
MVC слоистыйсередина,потому что Существует потребность во внешнем RPC вызов интерфейса, поэтому будет отдельный слой RPC Для инкапсуляции интерфейсов других сервисов. Этот слой лоскутный domain Уровень использования домена может быть определен adapter Интерфейс адаптера, посредством инверсии зависимостей, существует rpc реализация слоя domain Вызывающий интерфейс, определенный слоем.
также dao слой,существовать MVC Первоначально структура была относительно простой. Однако после трансформации необходимо будет изменить основные Redis использовать、Конфигурациясерединаиспользовать,Все мигрируют на уровень перемещения дао. потому что Исходная служба существования Для слоев доменного уровня вызывает эти базовые услуги, которые не доступны для проживания.,ии не соответствует Служитьфункциональные границыизразделять。
Подводить итоги,то естьот MVC приезжать DDD План реализации разборки для реконструкции архитектуры. Это самая дешевая и лучшая стратегия внедрения, которая полностью гарантирована. MVC структуру и может быть применен к DDD Преимущества архитектурной многослойности. Также можно использовать DDD Проектное мышление, ориентированное на предметную область, рефакторинг старого кода и повышение удобства сопровождения.
приезжать Здесь мы прояснили вопрос иерархической структуры. от MVC Изменить структуруприжаез DDD назад,Каково вызывающее звено инженерной модели середина? Далее расширяем архитектуру,Посмотрите на детали.
Далее мы поставим Многоуровневая архитектура DDD имеет плоскую структуру.,взгляниотодининтерфейс Реализацияприезжатькаждыймодульслоистыйсерединаизвызов链路关系是什么样из。таксуществоватьбудь собойизкодразвиватьсередина ХОРОШОссылкаприезжатьчто должно бытьиз Функциональное распределениеприезжатькакой модульсерединаиметь дело с。
отAPP-уровень、триггерный слой、приложениеслой,Эти три блока в основном инкапсулируют контекстную логику уровня домена и триггера (MQ, HTTP, JOB).,И, наконец, уровень существования приложения середина упаковывается и публикуется в Интернете. Эта часть посвящена обработке использования.,Так что слишком сложных операций не будет.
Когда начинается слой поля Входить,Это также начало проявления интеллекта середина. Все ваши абстрактные возможности для инженерии,Все отражено в этой области.
Возьми это Приходить Давайте представим этополеслойи Базаслойизмодуль Обязанности;Поток да слонов под картинкой середина,Вы можете обратить внимание на Вниз.
мы можем быть domain Уровень предметной области представляет собой структуру модели перегрузки, существующую domain На уровне домена может быть несколько пакетов домена. В идеале, конечно, если ваш DDD Разделите особенно чистый новый проект, а затем, возможно, domain Сразуодинполе。Но большойчасть Ши Вэй Служитьиз Разделение не будет столь детальным из соображений стоимости.,Также есть реконструкции некоторых старых проектов.,В одном проекте несколько направлений,Соответствующее решение — создать несколько иерархических пакетов одного уровня в рамках одного проекта. Например, пакет полей счета, пакет кредитных полей, пакет полей расчетов и т. д.,Агрегация внутри каждого пакета реализует разные функциональные возможности.
Пакет доменов в каждом домене включает в себя модель модели, складирование, интерфейс, обработку событий и услуг.
модель объекта модели;
repository Складские услуги;от базы данных и других источников данных для получения данных передаваемый объект может быть агрегатным объектом, объектом сущности, а возвращаемый результат может быть объектом сущности или объектом значения. потому что что Складские услуги состоят из базового слоя (инфраструктуры) Уровень эталонного домена (домен) представляет собой структуру инверсии зависимостей, но он может естественным образом изолировать объект персистентности базы данных PO от ссылок.
adapter Интерфейсные услуги — это другие услуги, которые полагаются на аутсорсинг; HTTP/RPC Вызов инкапсуляции интерфейса, существование через domain Уровень домена определяет интерфейс адаптера, который затем зависит от domain уровень инфраструктуры или отдельный дополнительный уровень, предназначенный для обработки интерфейсов для достижения domain Определите интерфейс адаптера для завершения зависимости HTTP/RPC Выполните обработку инкапсуляции.
event Сообщение о событии существует в качестве реализации службы, будут ситуации, когда сообщения будут отправляться наружу после завершения бизнеса. В настоящее время вы можете существовать модель домена середина, определить интерфейс сообщения о событии, а затем уровень Внимание завершает отправку сообщения.
service Проектирование сервисов; здесь вы должны быть осторожны, чтобы не инкапсулировать логику за пределами одного объекта только потому, что вы определили агрегатный объект. Это сделает ваш код все более трудным для поддержки в будущем. Агрегация должна сосредоточиться на одном простом сценарии инкапсуляции, связанном с этим объектом, и объединить некоторые основные бизнес-методы. service реализовано в。также, если ваши шаблоны проектирования применяются плохо;,Будь то проектирование на основе предметной области, проектирование на основе тестирования или переход на трехуровневую и четырехуровневую архитектуру.,тыизпроект Качество все равно будет очень плохим。
обеспечивает постоянство базы данных
、Установите Центр конфигурации Redisi для поддержки данных
、по отправке сообщения о событии
、поставлятьвнешний Служитьинтерфейс Инкапсуляция
。总之этот一слойизядерный Сердцеглазизто есть Дажехорошийиз Вспомогательный domain Уровень предметной области завершает разработку функций предметной области.
Вызывающий метод — инверсия зависимостей,такжето естьУровень обслуживания домена
определениеинтерфейс,уровень инфраструктуры
Выполняем реализацию функции。такможет быть эффективнымизизбегать Базауровень инфраструктурысерединаиз Объектиностранныйнезащищенный,Например, постоянные объекты базы данных.,существует такая многослойная структурасередина,Естественный защищенный базовый слой середина,Его нельзя внедрить извне.,В противном случае возникнет круговая зависимость.
При наличии этого слоя домен Слои не занимаются детальной обработкой данных. Перейти на уровень Метод середина занятых передает агрегатный объект или объект сущности через метод интерфейса. После существования уровня Партнерсередина завершает операцию транзакции данных. Он также будет включать запись в кеш Redis и отправку сообщений MQ после обработки транзакции. Если есть вопрос, который может похвастаться полем, это может быть таблица между базами данных. В настоящее время необходимо использовать. MQ Событийно-ориентированный.
Этот уровень относительно прост, состоит только из нескольких объектов параметров общего доступа. Ответ, а также объекты перечисления, объекты исключений и т. д. Предусмотрено использование внешнего интерфейсного уровня. Но если это RPC Для такого интерфейса рекомендуется RPC иностранныйпоставлятьизинтерфейсописывать Сумкасерединапоставлять,потому что только внешне, в комплект входит 1 легкая сумка и не зависит из любого другого пакета, который лучше всего поддерживать, управлять.
от MVC приезжать ДДД, есть одна вещь, которую мы должны четко понять.
от MVC приезжать DDD Мы просто переселились на дом побольше и с более четкой планировкой🏡, но это вас не определяетот MVC приезжать DDD Код становится очень чистым, красивым и аккуратным. потому что чтоот MVC приезжать DDD Изменился только скелет, а плоть и кровь под скелетом не изменились.
Если вы все же переместите исходный плохой код в новую многоуровневую архитектуру, это будет эквивалентно перемещению старой мебели, одежды, обуви и головных уборов из старого дома. Итак по дизайну программного обеспеченияизв принципе;Разделяй и властвуй, абстракция и знание,Знания середина — это применение принципов проектирования и шаблонов проектирования. Итак, если вы хотите хорошо писать код,Это должно быть сделаноDDD + шаблоны проектирования
,Только тогда вы сможете действительно хорошо написать код. Следующий,Брат Фу приведёт вам ещё один пример: использовать режим существования. DDD Аргументы в пользу рефакторинга в иерархических структурах.
дизайн программного Первый принцип обеспечения проживания, предложенный законом Конвея, разделяй и властвуй, абстракция и знание, являются руководящими инструкциями по проектированию и внедрению системы. Разделяй и властвуй и абстрактно, мы можем использовать DDD Рассматривается иерархическая архитектура мысленного картирования, но знания представляют собой применение принципов проектирования и шаблонов проектирования.
так,Если нет разумного использования знаний проектирования для улучшения кода,Поэтому даже если разделить границы процесса, сохранится четкая архитектура.,Также сложно создавать поддерживаемый код. Наиболее часто используемые шаблоны проектирования,Не что иное, как сочетание фабрик, стратегий и шаблонов;,Небольшое количество людей будет использовать режимы цепочки ответственности, строителя и комбинирования. Затем следующий,существование имеет общий шаблон проектирования с потокомиспользовать,让大家可以有一份可ссылкаизпроектдизайн кода。
Здесь мы настраиваем сценарий увеличения квоты. Думаю, все использовали кредитную карту 💳,Имеет начальную квоту,Последующее использованиесередина будет увеличиваться по мере накопления кредита и потребления.,Увеличьте сумму. Увеличение квоты требует серии проверочных решений и окончательного увеличения квоты. Процесс заключается в следующем;
Такая схема движения,Он наш партнер по развитию бизнеса.,Я часто смотрю приезжать. Сделайте ряд процессуальных суждений и процессов,Затем выполните определенную функцию. Проще говоря,то есть if···else Напишите код и проверьте его один за другим. Но со временем вы обнаружите, что код становится особенно запутанным. Основная причина заключается в том, что различные решения, принимаемые в поддержку завершения бизнеса, являются нестабильными факторами и будут постоянно корректироваться по мере изменения бизнеса. Иногда даже прямо отваливается. Но в вашем коде только что появилась одна ошибка // В компании говорят, что она пока не будет доступна, но вы не смеете ее удалять!
Сразу像有首歌唱из🎤:«Спрос остается на низком уровне,Ваш код становится все длиннее и длиннее. Звук конских копыт, исчезающий вдалеке,Вызовите свои ошибки. "
такдлятакиз Функциональное проектирование процесса,Что делать? Никогда не позволяйте копытам лошадей в пустыне,Продолжайте вытаскивать свою ошибку и бегите.
Один интерфейс, одна реализация и один код реализации.
Один кусок за другим, количество строк кода две-три тысячи.
Большинство из нас существуют MVC проектслоистыйструктура Вниз,Участвовать в разработке кода,По сути, он определяет интерфейс,Просто напишите кусок функционала для его реализации. Функциональная реализациясередина,Если посмотреть приезжать, то там уже готовый интерфейс.,Просто используйте его напрямую. Все реализации не будут основаны на интерфейсах, абстракциях, шаблонах и т. д.,такфинальныйтакизкод腐化изочень серьезно。
Перед реконструкцией сначала объясните новый процесс наслоения, как показано на рисунке;
Абстрактный класс — очень полезный класс. Один из них — определить структуру процесса, чтобы сделать код понятным и понятным. Другой вариант — определить общие методы, чтобы их можно было повторно использовать другими классами реализации.
Итак, здесь мы используем шаблон определения абстрактного класса + механизм правил, реализованный политикой и фабрикой, для обработки часто меняющегося процесса проверки класса и завершения разработки кода. Как показано на рисунке, сначала мы проектируем структуру реализации кода.
В режиме проектирования с тяжелой конструкционной обработкой существующий код отображается в следующей форме. —— Для дизассемблированного псевдокода вы можете обратиться к применению некоторых шаблонов проектирования в прошлом.
public AdjustAssetOrderEntity acceptAdjustAssetApply(AdjustAssetApplyEntity adjustAssetApplyEntity) {
// 1. Проверка параметров
this.parameterVerification(adjustAssetApplyEntity);
// 2. Запросить данные формы заявки. Если они были сохранены, они будут возвращены напрямую.
AdjustAssetOrderEntity orderEntity = queryAssetLog(adjustAssetApplyEntity.getPin(), adjustAssetApplyEntity.getAccountType(), adjustAssetApplyEntity.getTaskNo(), adjustAssetApplyEntity.getAdjustType());
if (null != orderEntity) {
log.info("pin={} taskNo={} Примите заявку и получите форму заявки на въезд в депозит, существующую в течение середина. ", adjustAssetApplyEntity.getUserId(), adjustAssetApplyEntity.getTaskNo());
return orderEntity;
}
// 3. Следующий процесс обрабатывается в рамках распределенной блокировки прибытия [избегая повторного запроса: Входить]
String lockId = genLockId(adjustAssetApplyEntity.getAdjustType(), adjustAssetApplyEntity.getUserId());
try {
// 3.1 Распределенная блокировка: блокировка
long state = lock(lockId);
if (0 == state) {
throw new AccountRuntimeException(BizResultCodeEm.DISTRIBUTED_LOCK_EXCEPTION.getCode(), "Исключение распределенной блокировки, обработка текущего поведения пользователя середина.");
}
// 3.2 Запрос на счет
UserAccountInfoDTO userAccountInfoDTO = queryJtAccount(adjustAssetApplyEntity.getUserId(), adjustAssetApplyEntity.getAccountType());
// 3.3 Базовая проверка (1) Тип счета, (2) Статус, (3) Тип квоты, (4) Просрочка по счету, (5) Тип ставки [Пока нет]
LogicCheckResultEntity logicCheckResultEntity = doCheckLogic(adjustAssetApplyEntity, userAccountInfoDTO,
DefaultLogicFactory.LogicModel.ACCOUNT_TYPE_FILTER.getCode(),
DefaultLogicFactory.LogicModel.ACCOUNT_STATUS_FILTER.getCode(),
DefaultLogicFactory.LogicModel.ACCOUNT_QUOTA_FILTER.getCode(),
DefaultLogicFactory.LogicModel.ACCOUNT_OVERDUE_FILTER.getCode()
);
if (!AssetCycleQuotaAlterCodeEnum.E0000.getCode().equals(logicCheckResultEntity.getCode())) {
log.info("userId={} taskNo={} Проверка правил, фильтрация и перехват. код:{} info:{}", adjustAssetApplyEntity.getUserId(), adjustAssetApplyEntity.getTaskNo(), logicCheckResultEntity.getCode(), logicCheckResultEntity.getInfo());
throw new AccountRuntimeException(logicCheckResultEntity.getCode(), logicCheckResultEntity.getInfo());
}
// 3.4 Принять корректировку квоты
return this.acceptAsset(adjustAssetApplyEntity, userAccountInfoDTO);
} finally {
// 3.1 Распределенная блокировка: разблокировать
this.unlock(lockId);
}
}
После этой обработки код становится очень понятным.
Я написал так много тематических проектов и разработал так много технических проектов. Вам приходится вручную создавать каждое из этих дел и проектов, которые делает Брат Фу? 🤔 Если вы не создаете каждый из них вручную, есть ли какой-нибудь инструмент повышения эффективности?
Нет, совсем нет. потому что что Брат Фу Иметь наборАртефакт!
для DDD Построение многомодульного проекта на самом деле является очень трудоемкой задачей, особенно построение распределенных проектов занимает еще больше времени. Однако создание инженерного модуля не требует слишком много времени. В основном время уходит на интеграцию различных распределенных компонентов, в том числе; MySQL, Redis, RocketMQ, Dubbo, shardingjdbc, XXL-JOB. Жду кучу технических рамок. Если вы когда-либо делали что-то подобное, вас обязательно убедит их использование и взаимодействие с различными версиями.
Настройка проекта и среды может занять от 6 до 8 часов! Но сегодня я хочу, чтобы вы сделали это одним щелчком пальцев!
Затем брат Фу представит этот набор инженерных строительных лесов с установкой вспомогательной среды, чтобы друзья могли ознакомиться с его использованием и быстро создать свои собственные учебные проекты.
В конце статьи приведены адреса для получения соответствующих инженерных лесов, а также учебные проекты, соответствующие инженерным лесам.
Это полный набор стандартов построения каркаса инженерного уровня, которые обеспечивают основу проекта и соответствующую полную инициализацию среды. Это позволяет партнерам по проектам разработки быстро выполнить основную работу и сэкономить от 6 до 8 часов усилий. Во-первых, давайте покажем общее содержание обучения.
Брат Фу Здесь поставляет 2 комплекта инженерных лесов, один комплект Облегченная версия используется без какого-либо распределенного технологического стека, другая версия — Стандартная. издание полностью использует стек распределенных технологий. потому что что很多小型项глаз并需要依赖太多из Распределенный стек технологий,И облегченное проектирование и разработка могут более эффективно повысить эффективность разработки. так,Читатели могут выбрать платформу, необходимую им для обучения, проверки и производства.
Облегченная версия платформы DDD, в основном предоставляющая службы HTTP. Он имеет высокую эффективность разработки и подходит для сценариев малого и среднего бизнеса.
Стандартная версия архитектуры DDD предназначена для решения задач развития бизнеса в средне- и крупномасштабных сценариях и комплексно использует стеки распределенных технологий для построения проектов.
Этот набор строительных лесов использует maven-archetype-plugin
использовать Заказ(archetype:create-from-project)руководитьпроектизстроительные лесасоздавать,После создания существующего внесите некоторые изменения в контент.,Окончательная опалубка для строительных лесов.
Когда читатели существуютиспользовать этот набор строительных лесов,Вы можете скачать код проекта локально.,Входитьпроект Внизиз scaffold-lite/std
выполнение модуля README.md середина mvn clean install
Скриптруководить Установить。Установитьназад Вот и всесуществоватьиспользовать IntelliJ IDEA При создании проекта выберите Maven Создайте и добавьте адрес локального склада для использования. —— Конкретные шаги будут представлены ниже👇🏻.
Как показано на картинке, есть два набора. DDD Проекты строительных лесов, под каждым набором проектов есть scaffold модуль. Эта часть в настоящее время соответствует модулю строительных лесов проекта. кроме того docs Есть dev-ops Папка представляет собой установочный пакет среды, который можно запустить напрямую. docker-compose.yml Вы можете установить всю среду сразу.
Читатели, вы также можете прекратить чтение во время чтения scaffold модуль。потому что Кроме этого модуля, остальные - это весь DDD Работает, как только вы впервые ознакомитесь с использованием. После ознакомления с существованием Входить"черный круг" README.md серединаосуществлять mvc clean install Таким образом, вы можете существовать локально maven На складе установлены строительные леса.
документ:docs/dev-ops/docker-compose.yml
docker-compose -f docker-compose.yml up -d
руководить Установить。
ЯсуществоватьучаствоватьНа третьем этапе специального тренировочного лагеря Tencent Technology Creation 2023 года будет проводиться конкурс сочинений. Соберите команду, чтобы выиграть приз!