**Введение: **Основная цель COLA — предоставить простой набор «руководств и ограничений» для архитектуры приложений, которые можно воспроизвести, понять, реализовать и контролировать сложность. На практике автор обнаружил, что COLA по-прежнему недостаточен в простоте, поэтому он сделал "апгрейд" для COLA. В этом обновлении не было добавлено никаких новых функций, но были максимально удалены некоторые понятия и функции, так что COLA более лаконичным и эффективным.
Недавно коллега сказал мне, что COLA как архитектура приложения была выбрана в качестве одного из вариантов архитектуры приложения для инициализации Java-приложений в Alibaba Cloud.
Это действительно что-то, поэтому на этом этапе я начал оглядываться назад и пересматривать достижения и потери COLA на этом пути.
COLA как архитектурная идея, несомненно, успешна. Но в качестве основы лично я чувствую себя немного безвкусно. В частности, простота не годится, и я чувствую, что сделал много лишнего.
Вы только представьте, какие-то функции я редко использую как автор, я вообще не понимаю, зачем они до сих пор существуют.
Основываясь на вышеизложенном, я сделал это обновление с COLA 2.0 до COLA 3.0. В этом обновлении я не добавлял никаких новых функций, а удалил как можно больше понятий и функций. Позвольте COLA больше сосредоточиться на архитектуре приложений, а не на поддержке фреймворка и архитектурных ограничениях.
За моими решениями стоит только одна причина - бритва Оккама.
принцип бритвы Оккама
Принцип бритвы Оккама, означающий, что Сущности не следует умножать без необходимости, то есть «простой и действенный принцип». Как говорит Оккам в Притчах 2, № 15: «Никогда не тратьте больше на то, чтобы делать, и используйте меньше, чтобы делать то же самое».
В конкретном процессе подачи заявки мы можем следовать следующим принципам:
«Если есть n теорий для одного и того же явления, самая простая из них является самой правильной. Если вы можете использовать n, чтобы сделать что-то хорошо, тогда не совершайте n + 1-е действие».
Например, носит ли император в «Новой одежде императора» одежду или нет? Если вы там, скорее всего, вы один из министров.
Если вы понимаете принцип бритвы Оккама, вы можете использовать логические средства, чтобы судить, кто является правдой.
-
Первая логика такова: предположим, что император действительно одет → предположим, что глупые люди не могут видеть → предположим, что вы глупы → значит, вы не видите императора в одежде;
-
Вторая логика такова: предположим, что у императора нет одежды → поэтому вы не видите императора в одежде.
Увидев и голого императора, второе объяснение простое и понятное. И первое объяснение, возможно потому, что оно неверное, требует большего количества предположений, чтобы исправить лазейки, точно так же, как и ложь.
Истину не нужно маскировать, она проста и ясна.
В качестве другого примера, геоцентрической теории и гелиоцентрической теории, модель геоцентрической теории Птолемея представляет собой модель эпицикла равноцикла. Согласно этой модели, люди могут количественно рассчитать движение планеты, а затем сделать вывод о ее местонахождении.
В позднем Средневековье, с постоянным совершенствованием приборов наблюдения, люди смогли более точно измерять положения и движения планет.Было замечено, что фактические положения планет отклонялись от результатов расчетов по этой модели. , они еле справлялись с этим.Позже число малых эпициклов увеличилось.до более чем восьмидесяти,но до сих пор не могут точно рассчитать точное положение планеты.
В 1543 году польский астроном Коперник на смертном одре опубликовал исторический труд — «О движении небесных тел». Эта теоретическая система выдвигает четкую точку зрения: Солнце является центром Вселенной, и все планеты вращаются вокруг Солнца. Теория утверждает, что Земля также является одной из планет, которая вращается как волчок, с одной стороны, и вращается вокруг Солнца, как и другие планеты, с другой.
Расчет Коперника не только строг по структуре, но и прост в расчетах.По сравнению с геоцентрической теорией, которая была добавлена к более чем 80 кругам, расчет Коперника может лучше соответствовать фактическим данным наблюдений. Поэтому геоцентрическая теория со временем была заменена гелиоцентрической теорией.
Изгибы в дизайне
Присмотревшись, в нашей системе действительно много конструкций, похожих на «геоцентрическую теорию».
С точки зрения системной архитектуры, некоторые изгибы и повороты связаны с необоснованным разделением системных границ, что приводит к нечетким обязанностям и запутанным зависимостям.
С точки зрения архитектуры приложения, некоторые повороты происходят из-за чрезмерного проектирования.Чтобы добиться так называемой гибкости и масштабируемости, используется неподходящий дизайн. В результате логика кода, которую можно представить интуитивно, упаковывается, скрывается и пересылается... Незаметно это сильно затрудняет читабельность и понятность кода и увеличивает стоимость сопровождения.
Например, я видел бесчисленное количество бизнес-систем и люблю говорить об оркестровке бизнес-процессов. Поэтому в бизнес-системе можно увидеть разнообразные «изгибающие конструкции».
Например, в бизнес-системе я видел следующую конструкцию конвейера. Суть этой схемы заключается в том, чтобы структурно разобрать сложную бизнес-операцию на несколько небольших процессорных единиц.
Разборка правильная, но такой подход явно не "оккамовский" (более структурированный разбор смотрите в другой моей статье:Как написать сложный бизнес-код?). Как сопровождающий, после входа в «функции входа» вы должны проверить базу данных, и тогда вы можете узнать, какие компоненты вызываются, что слишком сложно, недостаточно интуитивно понятно и не лаконично.
Та же логика, не ароматно ли писать следующим образом?
public class CreateCSPUExecutor {
@Resource
private InitContextStep initContextStep;
@Resource
private CheckRequiredParamStep checkRequiredParamStep;
@Resource
private CheckUnitStep checkUnitStep;
@Resource
private CheckExpiringDateStep checkExpiringDateStep;
@Resource
private CheckBarCodeStep checkBarCodeStep;
@Resource
private CheckBarCodeImgStep checkBarCodeImgStep;
@Resource
private CheckBrandCategoryStep checkBrandCategoryStep;
@Resource
private CheckProductDetailStep checkProductDetailStep;
@Resource
private CheckSpecImgStep checkSpecImgStep;
@Resource
private CreateCSPUStep createCSPUStep;
@Resource
private CreateCSPULogStep createCSPULogStep;
@Resource
private SendCSPUCreatedEventStep sendCSPUCreatedEventStep;
public Long create(MyCspuSaveParam myCspuSaveParam){
SaveCSPUContext context = initContextStep.initContext(myCspuSaveParam);
checkRequiredParamStep.check(context);
checkUnitStep.check(context);
checkExpiringDateStep.check(context);
checkBarCodeStep.check(context);
checkBarCodeImgStep.check(context);
checkBrandCategoryStep.check(context);
checkProductDetailStep.check(context);
checkSpecImgStep.check(context);
createCSPUStep.create(context);
createCSPULogStep.log(context);
sendCSPUCreatedEventStep.sendEvent(context);
return context.getCspu().getId();
}
}
public class CreateCSPUExecutor {
@Resource
private InitContextStep initContextStep;
@Resource
private CheckRequiredParamStep checkRequiredParamStep;
@Resource
private CheckUnitStep checkUnitStep;
@Resource
private CheckExpiringDateStep checkExpiringDateStep;
@Resource
private CheckBarCodeStep checkBarCodeStep;
@Resource
private CheckBarCodeImgStep checkBarCodeImgStep;
@Resource
private CheckBrandCategoryStep checkBrandCategoryStep;
@Resource
private CheckProductDetailStep checkProductDetailStep;
@Resource
private CheckSpecImgStep checkSpecImgStep;
@Resource
private CreateCSPUStep createCSPUStep;
@Resource
private CreateCSPULogStep createCSPULogStep;
@Resource
private SendCSPUCreatedEventStep sendCSPUCreatedEventStep;
public Long create(MyCspuSaveParam myCspuSaveParam){
SaveCSPUContext context = initContextStep.initContext(myCspuSaveParam);
checkRequiredParamStep.check(context);
checkUnitStep.check(context);
checkExpiringDateStep.check(context);
checkBarCodeStep.check(context);
checkBarCodeImgStep.check(context);
checkBrandCategoryStep.check(context);
checkProductDetailStep.check(context);
checkSpecImgStep.check(context);
createCSPUStep.create(context);
createCSPULogStep.log(context);
sendCSPUCreatedEventStep.sendEvent(context);
return context.getCspu().getId();
}
}
Этот метод написания прост, интуитивно понятен и прост в обслуживании.По сравнению с предыдущим методом, он имеет такую же возможность повторного использования компонентов. В соответствии с духом бритвы Оккама, для сравнения, изогнутый дизайн спереди, хотя и выглядит немного конструктивно, дает небольшие преимущества OCP. Однако стоимость понимания и познания повышена без всякой причины, нетрудно отличить, что лучше, а что хуже.
Обновление КОЛА 3.0
После столь долгой подготовки наконец пришло время покритиковать «изгибающийся дизайн» в COLA.
1. Удалить команду
На начальном этапе COLA из-за влияния CQRS мы думали об использовании командного режима для обработки запросов пользователей. Первоначальная цель дизайна состоит в том, чтобы использовать структуру, с одной стороны, для ограничения обработки команд и запросов, а с другой стороны, чтобы заставить логику службы быть разделенной на CommandExecutor, чтобы предотвратить чрезмерное расширение службы. быстро.
Подобно дизайну конвейера, представленному выше, этот дизайн немного запутан и не интуитивно понятен, как показано ниже:
public class MetricsServiceImpl implements MetricsServiceI{
@Autowired
private CommandBusI commandBus;
@Override
public Response addATAMetric(ATAMetricAddCmd cmd) {
return commandBus.send(cmd);
}
@Override
public Response addSharingMetric(SharingMetricAddCmd cmd) {
return commandBus.send(cmd);
}
@Override
public Response addPatentMetric(PatentMetricAddCmd cmd) {
return commandBus.send(cmd);
}
@Override
public Response addPaperMetric(PaperMetricAddCmd cmd) {
return commandBus.send(cmd);
}
}
Это выглядит довольно чисто, но не интуитивно понятно, какой именно Executor обрабатывает ATAMetricAddCmd. Я также должен понимать CommandBus и то, как CommandBus регистрирует Executors. Невидимо увеличивает когнитивные затраты, что не есть хорошо.
В таком случае, почему бы не использовать бритву Оккама, чтобы убить этот CommandBus. Как показано ниже, после удаления CommandBus код стал ненамного более интуитивным, единственная потеря в том, что мы потеряем функцию Interceptor, предоставляемую уровнем фреймворка, однако именно Interceptor находится там, где я хочу переместить нож.
public class MetricsServiceImpl implements MetricsServiceI{
@Resource
private ATAMetricAddCmdExe ataMetricAddCmdExe;
@Resource
private SharingMetricAddCmdExe sharingMetricAddCmdExe;
@Resource
private PatentMetricAddCmdExe patentMetricAddCmdExe;
@Resource
private PaperMetricAddCmdExe paperMetricAddCmdExe;
@Override
public Response addATAMetric(ATAMetricAddCmd cmd) {
return ataMetricAddCmdExe.execute(cmd);
}
@Override
public Response addSharingMetric(SharingMetricAddCmd cmd) {
return sharingMetricAddCmdExe.execute(cmd);
}
@Override
public Response addPatentMetric(PatentMetricAddCmd cmd) {
return patentMetricAddCmdExe.execute(cmd);
}
@Override
public Response addPaperMetric(PaperMetricAddCmd cmd) {
return paperMetricAddCmdExe.execute(cmd);
}
}
2. Удалить перехватчик
В то время Interceptor был разработан, потому что в качестве основы использовался CommandBus.Чтобы лучше использовать преимущества командного режима, была добавлена функция Interceptor. Его сущностью является процесс АОП.
Учитывая, что АОП-функция Spring уже совершенна, этот дизайн также немного безвкусен. Оказывается, когда люди используют фреймворк COLA, они редко используют Interceptor, в том числе и я. В таком случае удалите его.
3. Удалить конвертер, валидатор, ассемблер
Здесь не будем повторяться о важности именования. В то время я думал о том, чтобы стандартизировать наименования некоторых часто используемых функций на уровне фреймворка. Однако на практике эта идея оказывается слишком идеалистичной.
Я помню, что в первые дни практики команды COLA часто возникали споры о том, почему Convertor (конвертер) и что такое Assembler (ассемблер).
Позже я тщательно обдумал это.Хотя название очень важно, его область применения - это в лучшем случае спецификация команды. Нет никакой принципиальной разницы между тем, называется ли ваш валидатор Валидатором или Чекером. Команда определяет это сама. Попытка решить проблему командного согласия с уровня фреймворка будет не очень хорошей, поэтому ее также решительно исключают.
4. Оптимизация сканирования классов
Идея бизнес-идентичности и точек расширения является основной концепцией TMF, а также основной методологией мультисервисной поддержки в бизнес-мидл-офисе Alibaba.
COLA стремится предоставить облегченную реализацию расширения, поэтому эта функциональность остается под ножом мясника Оккама. Поскольку дизайн точки расширения COLA основан на TMF средней ступени, в предыдущем проекте его схема сканирования классов заключается в том, чтобы напрямую копировать подход TMF.
На самом деле схема сканирования классов TMF немного избыточна для COLA. Потому что сама COLA построена на основе Spring, а Spring построен на основе сканирования классов. Поэтому мы можем полностью повторно использовать сканирование классов Spring, и нам не нужно писать свои собственные.
В нативном Spring есть по крайней мере три способа получить bean-компонент пользовательской аннотации, наиболее лаконичный — использовать метод ListableBeanFactory.getBeansWithAnnotation или использовать ClassPathScanningCandidateComponentProvider для сканирования пакета.
В этой версии я выбрал метод getBeansWithAnnotation, в основном для получения Bean-компонента @Extension, который используется для реализации функции точки расширения, и отказался от исходной реализации сканирования классов TMF.
Суммировать
Мотивация запуска этого обновления в основном связана с тем, что в процессе практики COLA я обнаружил несколько ярких функций. Когда COLA, как базовая архитектура приложений Alibaba Cloud, имеет растущее влияние, я обязан дать всем правильное руководство — удалить ложное и сохранить истинное, быть кратким и эффективным, а не усложнять.
Фактически, COLA состоит из двух частей:
С одной стороны, COLA — это архитектурная идея, архитектура приложения, объединяющая такие архитектурные идеи, как луковичная кольцевая архитектура, архитектура адаптера, DDD, чистая архитектура и TMF.
В этом обновлении архитектурная идея в основном не изменилась, единственный момент заключается в том, что, поскольку концепция Command удалена, CQRS стал опцией, а не строгим требованием.
С другой стороны, COLA также является компонентом фреймворка.В ходе этого обновления я использовал бритву Оккама, чтобы отрезать большую часть возможностей компонента, и сохранил только функцию точки расширения. Цель состоит в том, чтобы COLA как платформа не накладывала слишком много ограничений на разработчиков приложений, что не соответствует простому и эффективному стилю.
Итак, если подытожить, то это не столько апгрейд, сколько функциональный «даунгрейд», вычитание.
Но я считаю, что вычитание может сделать COLA более соответствующим духу Оккама и помочь COLA пойти дальше.
Адрес открытого исходного кода COLA:GitHub.com/Alibaba/col…
Создание каркаса облачных JAVA-приложений Alibaba
start.aliyun.com — платформа для генерации инженерных строительных лесов на основе Spring-initializr. Разработчикам нужно только добавить некоторые аннотации и небольшое количество настроек, чтобы быстро построить распределенную систему приложений. Она использует более дружественный китайский язык и не имеет сетевой задержки. проблемы и, что наиболее важно, обеспечивают более локализованные зависимости компонентов.
Щелкните ссылку, чтобы сразу же ознакомиться с созданием шаблонов приложений Alibaba Cloud JAVA:start.aliyun.com/?UTM_con десять…
"Облачная нативная платформа AlibabaСосредоточьтесь на микросервисах, бессерверных технологиях, контейнерах, Service Mesh и других технических областях, сосредоточьтесь на популярных тенденциях облачных технологий и практиках крупномасштабного внедрения облачных технологий, а также станьте официальной учетной записью самых облачных разработчиков. "