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:

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:

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:

Manual vs. Automatic Patterns

Traditional frameworks required manual implementation of:

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:

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:

To the reality of feature parity:

Development Time Impact

This code difference shows the development impact for business applications:

Development Time Explosion:

Maintenance Nightmare:

Common Feature Trade-offs: Applications may ship without:

Conclusion

This analysis shows that many frameworks require developers to manually implement patterns that could be framework-provided, resulting in:

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 →