Codion and Complexity
Complexity Comparison Study
CRUD Implementation Comparison
To compare implementation approaches, an experiment was conducted comparing minimal CRUD implementations across various frameworks. The baseline was a simple customer management application with:
- A Customer entity (id, first_name)
- Full CRUD operations
- Search functionality
- A master-detail relationship with Address entity (id, customer_id, street)
Codion Implementation
The complete Codion implementation required just over 140 lines of code:
package is.codion.demos.store;
import is.codion.common.db.database.Database;
import is.codion.common.user.User;
import is.codion.framework.db.EntityConnectionProvider;
import is.codion.framework.db.local.LocalEntityConnectionProvider;
import is.codion.framework.domain.DomainModel;
import is.codion.framework.domain.DomainType;
import is.codion.framework.domain.entity.EntityType;
import is.codion.framework.domain.entity.attribute.Column;
import is.codion.framework.domain.entity.attribute.Column.Generator;
import is.codion.framework.domain.entity.attribute.ForeignKey;
import is.codion.swing.framework.model.SwingEntityApplicationModel;
import is.codion.swing.framework.model.SwingEntityEditModel;
import is.codion.swing.framework.model.SwingEntityModel;
import is.codion.swing.framework.ui.EntityApplication;
import is.codion.swing.framework.ui.EntityApplicationPanel;
import is.codion.swing.framework.ui.EntityEditPanel;
import is.codion.swing.framework.ui.EntityPanel;
import java.util.List;
import static is.codion.framework.domain.DomainType.domainType;
public final class StoreAppPanel extends EntityApplicationPanel<SwingEntityApplicationModel> {
public static final DomainType DOMAIN = domainType("Store");
public interface Customer {
EntityType TYPE = DOMAIN.entityType("store.customer");
Column<Long> ID = TYPE.longColumn("id");
Column<String> FIRST_NAME = TYPE.stringColumn("first_name");
}
public interface Address {
EntityType TYPE = DOMAIN.entityType("store.address");
Column<Long> ID = TYPE.longColumn("id");
Column<String> STREET = TYPE.stringColumn("street");
Column<Long> CUSTOMER_ID = TYPE.longColumn("customer_id");
ForeignKey CUSTOMER_FK = TYPE.foreignKey("customer_fk", CUSTOMER_ID, Customer.ID);
}
private static class Store extends DomainModel {
private Store() {
super(DOMAIN);
add(Customer.TYPE.define(
Customer.ID.define()
.primaryKey()
.generator(Generator.identity()),
Customer.FIRST_NAME.define()
.column()
.caption("First name")
.nullable(false)
.maximumLength(40))
.caption("Customer")
.build());
add(Address.TYPE.define(
Address.ID.define()
.primaryKey()
.generator(Generator.identity()),
Address.STREET.define()
.column()
.nullable(false)
.maximumLength(120)
.caption("Street"),
Address.CUSTOMER_ID.define()
.column(),
Address.CUSTOMER_FK.define()
.foreignKey()
.caption("Customer"))
.caption("Address")
.build());
}
}
public StoreAppPanel(SwingEntityApplicationModel applicationModel) {
super(applicationModel, createEntityPanels(applicationModel), List.of());
}
private static List<EntityPanel> createEntityPanels(SwingEntityApplicationModel applicationModel) {
SwingEntityModel customerModel = applicationModel.entityModels().get(Customer.TYPE);
SwingEntityModel addressModel = customerModel.detailModels().get(Address.TYPE);
EntityPanel customerPanel = new EntityPanel(customerModel, new CustomerEditPanel(customerModel.editModel()));
EntityPanel addressPanel = new EntityPanel(addressModel, new AddressEditPanel(addressModel.editModel()));
customerPanel.detailPanels().add(addressPanel);
return List.of(customerPanel);
}
private static SwingEntityApplicationModel createApplicationModel(EntityConnectionProvider connectionProvider) {
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityModel addressModel = new SwingEntityModel(Address.TYPE, connectionProvider);
customerModel.detailModels().add(addressModel);
return new SwingEntityApplicationModel(connectionProvider, List.of(customerModel));
}
private static class CustomerEditPanel extends EntityEditPanel {
public CustomerEditPanel(SwingEntityEditModel editModel) {
super(editModel);
}
@Override
protected void initializeUI() {
focus().initial().set(Customer.FIRST_NAME);
createTextField(Customer.FIRST_NAME);
addInputPanel(Customer.FIRST_NAME);
}
}
private static class AddressEditPanel extends EntityEditPanel {
public AddressEditPanel(SwingEntityEditModel editModel) {
super(editModel);
}
@Override
protected void initializeUI() {
focus().initial().set(Address.CUSTOMER_FK);
createTextField(Address.STREET);
createComboBox(Address.CUSTOMER_FK);
addInputPanel(Address.STREET);
addInputPanel(Address.CUSTOMER_FK);
}
}
public static void main(String[] args) {
Database.URL.set("jdbc:h2:mem:h2db");
Database.INIT_SCRIPTS.set("src/main/sql/create_schema.sql");
EntityApplication.builder(SwingEntityApplicationModel.class, StoreAppPanel.class)
.connectionProvider(LocalEntityConnectionProvider.builder()
.domain(new Store())
.user(User.parse("scott:tiger"))
.build())
.model(StoreAppPanel::createApplicationModel)
.start();
}
}
This implementation includes:
- Full CRUD operations for both entities
- Master-detail filtering (automatic)
- Search functionality
- Form validation
- Keyboard navigation
- Valid/modified indicators
- Multi-item editing
- Column filtering and sorting
- Denormalized export capabilities
- Responsive table components
Framework Comparison Results
Framework | Lines of Code | Complexity | Master-Detail Impact | Cognitive Load |
---|---|---|---|---|
Codion | 143 | Low (3.5/10) | +51 lines (+55%) | Minimal |
Spring Boot + Thymeleaf | ~355 | High (7/10) | +170 lines (+92%) | High |
JavaFX + Spring Boot | ~500 | Very High (8.5/10) | +210 lines (+72%) | Very High |
Vaadin + Spring Boot | ~335 | Medium-High (6.5/10) | +155 lines (+86%) | Medium-High |
React + Node.js | ~420 | High (7.5/10) | +180 lines (+75%) | High |
Angular + .NET Core | ~480 | Very High (8/10) | +200 lines (+71%) | Very High |
Key Observations
Complexity Explosion
Adding a simple master-detail relationship revealed dramatic differences:
- Codion: Added just 51 lines with automatic filtering and synchronization
- Other frameworks: Required 155-210 additional lines with manual coordination
- Average complexity increase: Codion 55% vs. others 81%
Manual vs. Automatic Patterns
Traditional frameworks required manual implementation of:
- Master-detail selection coordination
- Filtering detail records by master
- Refresh patterns after updates
- State management between entities
- Visibility toggling for UI sections
- Error handling duplication
Codion handles these patterns automatically through its framework design.
Feature Parity Gap
Despite having 2-3x more code, other frameworks still lacked many of Codion’s built-in features:
- Comprehensive keyboard navigation
- Valid/modified attribute indicators
- Multi-item editing capabilities
- Advanced per-column filtering
- Export functionality
- Automatic UI generation from domain model
Feature Parity Requirements
Here’s what’s needed to achieve feature parity with Codion using other frameworks:
Basic Business Table Requirements
Feature | Lines of Code | Framework Implementation |
---|---|---|
Sortable columns | ~50 | Custom sort logic, state management, UI indicators |
Resizable columns | ~100 | Mouse event handlers, column width calculations, persistence |
Column reordering | ~150 | Drag & drop implementation, array manipulation, state sync |
Per-column filtering | ~200 | Filter UI components, query building, debounced search |
Keyboard navigation | ~300 | Key event handlers, focus management, selection logic |
Export functionality | ~150 | Data serialization, clipboard API, format options |
Multi-row selection | ~100 | Selection state, Ctrl/Shift logic, visual indicators |
Modified field indicators | ~100 | Dirty checking, visual states, form coordination |
Master-detail coordination | ~500 | Manual filtering, refresh orchestration, state sync |
Professional validation | ~200 | Field-level validation, error display, form states |
Total additional lines needed: ~1,850
This transforms our comparison from:
- Codion: 143 lines
- Other frameworks: ~355-510 lines
To the reality of feature parity:
- Codion: 143 lines (full enterprise functionality)
- Other frameworks: 355-510 + 1,850 = 2,205-2,360 lines
Development Time Impact
This code difference shows the development impact for business applications:
Development Time Explosion:
- Basic CRUD: 3-5 days with any framework
- Professional business features: 6-8 weeks additional development
- Testing all interaction combinations: 2-3 weeks
- Cross-browser compatibility: 1-2 weeks
- Performance optimization: 1-2 weeks
Maintenance Nightmare:
- Every framework upgrade potentially breaks custom table logic
- Browser updates require compatibility testing
- Feature additions require modifying multiple interconnected systems
- Bug fixes ripple through hand-rolled component hierarchies
Common Feature Trade-offs: Applications may ship without:
- Column resizing (“users can work around it”)
- Keyboard navigation (“they can use the mouse”)
- Export functionality (“we’ll add it later”)
- Proper validation feedback (“basic validation is enough”)
Conclusion
This analysis shows that many frameworks require developers to manually implement patterns that could be framework-provided, resulting in:
- 16x more code for equivalent functionality
- Months of additional development for basic professional features
- Ongoing maintenance complexity with custom component libraries
- User experience compromises that impact productivity
- Increased training requirements for both developers and end users
Codion takes a different approach - optimizing specifically for business application requirements. Its development has focused on business application needs. Explore the complete Codion philosophy →
These findings suggest that some internal business applications could benefit from desktop implementations using domain-focused frameworks. This presents an alternative to web deployment for certain enterprise tools. Read why desktop applications still matter →