Why COLA 3.0 Cuts Complexity: An Ockham’s Razor‑Driven Redesign
The article reviews the COLA framework’s evolution to version 3.0, explaining how the author applied Ockham’s razor to remove unnecessary commands, interceptors, converters, validators and class‑scanning logic, resulting in a leaner, more maintainable Java backend architecture.
Background
COLA (Component Oriented Lightweight Architecture) is an application‑architecture framework that aims to provide a simple, reproducible and controllable set of guidelines. The author found that the original design contained many superfluous concepts, so COLA 2.0 was upgraded to COLA 3.0 by removing unnecessary features without adding new functionality.
Ockham’s Razor as the Guiding Principle
The upgrade follows Ockham’s razor – “entities should not be multiplied unnecessarily”. The simplest solution that works is preferred, avoiding extra abstraction and indirection.
Design Pitfalls: Over‑Design and Twisty Logic
From a system‑boundary perspective, unclear responsibilities and excessive abstraction lead to “twisty” designs. From an application‑architecture view, over‑design for flexibility creates wrappers, hidden layers and pipelines that hurt readability and increase maintenance cost.
Example Pipeline (original)
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();
}
}The author argues that while reusable, this approach adds unnecessary cognitive load. A simpler, more direct implementation is preferred.
Removing the Command Pattern
Initially COLA adopted CQRS with a Command‑Bus to separate commands and queries, but the indirection made the actual executor hidden.
Original command‑bus version
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); }
}After removing the bus, the service directly injects the executors.
After removal
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); }
}Dropping Interceptors, Converters, Validators, and Assemblers
The framework originally provided interceptor hooks, converters, validators and assemblers to standardise cross‑cutting concerns. Spring’s native AOP already covers most interceptor needs, and naming conventions (Validator vs. Checker) are team‑level decisions. Therefore these components were removed.
Class‑Scanning Optimization
COLA previously copied the TMF (Taobao Middleware Framework) class‑scanning mechanism, which is redundant because Spring already performs classpath scanning. The upgrade switches to Spring’s ListableBeanFactory.getBeansWithAnnotation to discover beans annotated with @Extension, eliminating the custom scanner.
Overall Impact of the Upgrade
The core architectural ideas (onion, adapter, DDD, clean architecture, TMF) remain unchanged. CQRS becomes optional, the command bus is removed, and many auxiliary components are stripped away, leaving a lightweight extension‑point model that aligns with Ockham’s razor.
COLA open‑source address: https://github.com/alibaba/COLA
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
