The Reality Check

You know that feeling when you inherit a 35,000-line Spring Boot monster that does basic CRUD? Or when you realize your “modern” web stack needs 47 dependencies to display a table? Codion exists because we’ve all been there.

This isn’t another framework promising to “revolutionize development.” This is battle-tested code that’s kept critical infrastructure running for decades—from marine research vessels to national fishing quota systems. While others chase the latest JavaScript trend, Codion has been quietly solving real problems.


🧬 Observable Architecture: Everything Reacts

Forget manual event handling. In Codion, your UI automatically stays in sync because everything is observable.

// State that automatically controls UI elements
State processing = State.state();
State hasData = State.state();
State canSubmit = State.and(hasData, processing.not());

// Values that propagate changes throughout your app
Value<String> searchFilter = Value.builder()
    .nullable()
    .listener(this::updateResults)
    .build();

// UI components that bind to state - no manual wiring
JButton submitButton = button(submitAction)
    .enabled(canSubmit)
    .build();

Your code describes what should happen, not how to keep everything synchronized. Change a value anywhere, and every dependent UI element updates automatically. No Redux. No useState hooks. No callback hell.


🏗️ Domain-First Design: Your Business Logic Runs Everything

Define your domain once. Get CRUD operations, UI forms, validation, and queries for free.

// This isn't just a schema - it's executable business logic
interface Customer {
    EntityType TYPE = DOMAIN.entityType("store.customer");
    
    Column<String> EMAIL = TYPE.stringColumn("email");
    Column<String> PHONE = TYPE.stringColumn("phone");
    Column<BigDecimal> CREDIT_LIMIT = TYPE.bigDecimalColumn("credit_limit");
    Column<Integer> ADDRESS_ID = TYPE.integerColumn("address_id");
    
    // Foreign keys are first-class citizens, not afterthoughts
    ForeignKey ADDRESS_FK = TYPE.foreignKey("address_fk", ADDRESS_ID, Address.ID);
}

// Configure behavior once, use everywhere
Customer.EMAIL.define()
    .column()
    .caption("Email")
    .nullable(false)
    .searchable(true)
    .maximumLength(255);

Customer.ADDRESS_FK.define()
    .foreignKey()
    .caption("Address");

This definition automatically generates type-safe CRUD operations, form validation and UI components, search and filter capabilities, foreign key navigation and loading, and database query optimizations.

No separate DTOs. No mapping layers. No synchronization headaches.


🚀 Performance That Actually Scales

While your React app chokes on 1000 rows, Codion applications are built for serious performance. Load testing proves 8000+ concurrent users capability, though real-world usage in Iceland typically involves dozens of concurrent users (it’s a small country, after all).

Load Test Results

Real metrics from production systems: Marine research vessel with 24/7 uptime collecting biological measurements. National catch registration system handling entire country’s fishing industry. Scientific databases serving researchers for over two decades.

// Built-in load testing because performance matters
LoadTest.builder(applicationFactory, Application::close)
    .user(TEST_USER)
    .scenarios(List.of(
        scenario(new InsertCustomer(), 1),      // Weight: 1
        scenario(new SearchCustomers(), 10),    // Weight: 10
        scenario(new GenerateReport(), 2)))     // Weight: 2
    .build();

Why it’s fast: The database layer essentially boils down to HashMap lookups and string concatenation. Domain metadata enables intelligent query optimization where the framework knows exactly which columns to fetch, how to join tables efficiently, and when to cache results - all without runtime reflection or complex object mapping.


🎯 Desktop UI That Doesn’t Suck

Rich components. Keyboard navigation. Real accessibility. Everything your users actually want.

// Tables that handle thousands of rows without breaking a sweat
FilterTable<Customer, CustomerColumn> table = FilterTable.builder(model, columns)
    .sortable(true)
    .autoResizeMode(AUTO_RESIZE_ALL_COLUMNS)
    .doubleClick(command(this::editCustomer))
    .keyEvent(KeyEvents.builder(VK_DELETE)
        .condition(WHEN_FOCUSED)
        .action(command(this::deleteSelected)))
    .build();

// Forms that validate in real-time
JTextField emailField = stringField(customer.email())
    .validator(EmailValidator.INSTANCE)
    .transferFocusOnEnter(true)
    .selectAllOnFocusGained(true)
    .build();

Keyboard-first design with Enter transferring focus by default and plenty of shortcuts for power users. Master-detail relationships with fractal UI that scales to any complexity. Real-time validation that catches errors before users hit save. Network resilience with graceful handling of connection issues.


🔗 Foreign Keys as First-Class Citizens

Stop writing join queries. Stop managing object relationships. Let Codion handle it.

// Define relationships once
interface Order {
    ForeignKey CUSTOMER_FK = TYPE.foreignKey("customer_fk", CUSTOMER_ID, Customer.ID);
    ForeignKey SHIPPING_ADDRESS_FK = TYPE.foreignKey("shipping_fk", SHIPPING_ID, Address.ID);
}

// Use them everywhere
Entity order = connection.selectSingle(Order.ID.equalTo(42));
Entity customer = order.get(Order.CUSTOMER_FK);        // Already loaded
String customerName = customer.get(Customer.NAME);     // No extra queries
String customerEmail = customer.get(Customer.EMAIL);   // Still no extra queries

// Control loading depth for performance
Order.CUSTOMER_FK.define()
    .foreignKey()
    .attributes(Customer.NAME, Customer.EMAIL)  // Only fetch what you need
    .referenceDepth(2);                         // Customer -> Address -> Country

Your code reads like business logic, not database plumbing.


💾 Database Agnostic (Actually)

Support for databases you’ve heard of, with battle-tested implementations.

Production-proven:

Production-ready:

// Same code, any database
EntityConnection connection = connectionProvider.connection();

// Type-safe queries that work everywhere
List<Entity> activeCustomers = 
    connection.select(
        and(Customer.ACTIVE.equalTo(true),
            Customer.REGISTRATION_DATE.greaterThan(lastMonth)));

// Transactions that actually work
EntityConnection.transaction(connection, () -> {
    Entity customer = connection.insert(newCustomer);
    Entity order = connection.entities().builder(Order.TYPE)
        .with(Order.CUSTOMER_FK, customer)
        .build();
    connection.insert(order);
    connection.insert(orderLines);
    // Automatic rollback on any exception
});

🧪 Testing That Doesn’t Hate You

Clear separation between business logic and UI. Real tests from real applications.

// Test domain model structure (from Petstore demo)
public class PetstoreTest extends DomainTest {

    public PetstoreTest() {
        super(new Petstore());
    }

    @Test
    void address() {
        test(Address.TYPE);  // Tests CRUD operations, constraints, foreign keys
    }

    @Test
    void product() {
        test(Product.TYPE);  // Validates entity definition and relationships
    }

    @Test
    void item() {
        test(Item.TYPE);     // Verifies domain integrity automatically
    }
}

// Test domain validation (from Petclinic demo)
@Test
void validation() {
    try (EntityConnectionProvider connectionProvider = createConnectionProvider()) {
        EntityConnection connection = connectionProvider.connection();
        VetSpecialtyEditModel model = new VetSpecialtyEditModel(connectionProvider);

        Entity linda = connection.selectSingle(Vet.FIRST_NAME.equalTo("Linda"));
        Entity surgery = connection.selectSingle(Specialty.NAME.equalTo("surgery"));

        model.editor().value(VetSpecialty.VET_FK).set(linda);
        model.editor().value(VetSpecialty.SPECIALTY_FK).set(surgery);
        
        // Business rule: Linda already specializes in surgery
        assertThrows(ValidationException.class, model::insert);
    }
}

// Test table model behavior (from Chinook demo)
@Test
void raisePriceOfSelected() {
    try (EntityConnectionProvider connectionProvider = createConnectionProvider()) {
        Entity masterOfPuppets = connectionProvider.connection()
                .selectSingle(Album.TITLE.equalTo("Master Of Puppets"));

        TrackTableModel trackTableModel = new TrackTableModel(connectionProvider);
        trackTableModel.queryModel().condition()
            .get(Track.ALBUM_FK).set().equalTo(masterOfPuppets);

        trackTableModel.items().refresh();
        assertEquals(8, trackTableModel.items().visible().count());

        trackTableModel.selection().selectAll();
        trackTableModel.raisePriceOfSelected(BigDecimal.ONE);

        // Verify all tracks now cost $1.99
        trackTableModel.items().get().forEach(track ->
            assertEquals(BigDecimal.valueOf(1.99), track.get(Track.UNITPRICE)));
    }
}

Testing philosophy:


🎨 Modern Look & Feel

Beautiful, modern themes that users actually want to look at. No more gray boxes from 1995.

// Include FlatLaF plugin for modern flat themes
<dependency>
    <groupId>is.codion</groupId>
    <artifactId>codion-plugin-flatlaf</artifactId>
    <version>0.18.38</version>
</dependency>

// Add 40+ IntelliJ-based themes 
<dependency>
    <groupId>is.codion</groupId>
    <artifactId>codion-plugin-flatlaf-intellij-themes</artifactId>
    <version>0.18.38</version>
</dependency>

What you get:

Popular theme collections:

Built-in theme management: Every Codion application automatically includes a View → Select look and feel menu item that opens a theme selector. The combo box shows each theme name with a visual preview of its key colors, making it easy for users to see what each theme looks like before applying it.

// Users can change themes instantly via UI
LookAndFeelSelectionDialogBuilder.lookAndFeelSelectionDialog()
    .owner(parentFrame)
    .show();

The experience: Select a new theme from the dropdown and watch your entire application instantly transform - menus, buttons, tables, forms, everything updates immediately. No restart required, no flickering, just smooth transitions to your preferred aesthetic.

Why this matters: Your desktop applications look professional and modern. Users can customize their experience with themes they love. No more apologizing for “legacy desktop UI” - these applications look better than most web apps.


🤖 5GL Ready: Built for AI

Codion’s API design makes it perfect for AI-assisted development. Every method takes parameters, every pattern is consistent, every abstraction is discoverable.

// This API is LLM-friendly because it's parameter-based
StringField.builder()
    .value(customer.email())
    .nullable(false)                    // Instead of nullable()
    .transferFocusOnEnter(true)        // Instead of transferFocusOnEnter()
    .selectAllOnFocusGained(true)      // Instead of selectAllOnFocusGained()
    .validator(EmailValidator.INSTANCE)
    .build();

// AI can mechanically generate this from UI mockups or requirements
FilterTable.builder(model, columns)
    .sortable(true)
    .selectionMode(SINGLE_SELECTION)
    .autoResizeMode(AUTO_RESIZE_ALL_COLUMNS)
    .build();

What this enables:


📦 Deployment: From Local to Enterprise

Local development: Embedded H2 database, single JAR deployment
Team development: PostgreSQL server, RMI connections
Enterprise deployment: Oracle backend, thousands of concurrent users

// Same application code, different connection strategies
Database database = new H2DatabaseFactory()
    .create("jdbc:h2:mem:h2db;DB_CLOSE_DELAY=-1");

EntityConnectionProvider localProvider = 
    LocalEntityConnectionProvider.builder()
        .domain(new StoreDomain())
        .database(database)
        .build();

EntityConnectionProvider remoteProvider = 
    RemoteEntityConnectionProvider.builder()
        .domainType(Store.DOMAIN)
        .hostName("production-server")
        .port(1142)
        .build();

Deployment options:


⚡ Migration Stories: From Mess to Maintainable

Real migration: 35,000 lines of spaghetti code → 21,000 lines of clean Codion. 4 months. Still running over a decade later.

// Before: Spring Boot controller hell
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
    @Autowired CustomerService customerService;
    @Autowired AddressService addressService;
    @Autowired OrderService orderService;
    
    @GetMapping("/{id}")
    public ResponseEntity<CustomerDTO> getCustomer(@PathVariable Long id) {
        Customer customer = customerService.findById(id)
            .orElseThrow(() -> new CustomerNotFoundException(id));
        Address address = addressService.findByCustomerId(id);
        List<Order> orders = orderService.findByCustomerId(id);
        
        CustomerDTO dto = new CustomerDTO();
        dto.setId(customer.getId());
        dto.setName(customer.getName());
        dto.setEmail(customer.getEmail());
        // ... 50 more lines of mapping
        
        return ResponseEntity.ok(dto);
    }
}

// After: Codion just works
Entity customer = connection.selectSingle(Customer.ID.equalTo(customerId));
String name = customer.get(Customer.NAME);
String email = customer.get(Customer.EMAIL);
Entity address = customer.get(Customer.ADDRESS_FK);  // Automatic loading

Migration path:

  1. Define domain entities from existing schema
  2. Replace repositories with EntityConnection
  3. Migrate forms to EntityEditPanel
  4. Replace tables with EntityTablePanel
  5. Delete boilerplate (lots of it)

Complexity over Time

A decade of refinement shows in the metrics:

Recent improvements:


🛠️ Developer Experience

IDE Integration

Code Generation

Documentation


🎭 The Three Personalities

Codion adapts to your needs:

1. Pure UI Framework (SDKBOY demo)

Just the codion-swing-common-ui module and deps. No CRUD. Perfect for tools and utilities.

2. Hybrid Approach (Llemmy demo)

Minimal entities for persistence, custom UI for everything else. Best of both worlds.

3. Full Framework (Chinook demo)

Complete domain-driven CRUD with generated UIs. Maximum productivity for business applications.


🎯 Who Should Use Codion

Perfect for:

Not ideal for:


🚦 Getting Started

# Add to your Maven project
<dependency>
    <groupId>is.codion</groupId>
    <artifactId>codion-swing-framework-ui</artifactId>
    <version>0.18.38</version>
</dependency>

# Or your Gradle project  
implementation 'is.codion:codion-swing-framework-ui:0.18.38'

Next steps:

  1. Try the Petclinic demo - Basic CRUD patterns
  2. Explore the manual - Deep dive into concepts
  3. Check the World demo - Advanced domain modeling
  4. Study Chinook - Kitchen sink example

📜 License: Open Source, Commercially Friendly

GPL-3.0 with Commercial Linking Exception


Codion: The framework you wish you’d found years ago.

Battle-tested by 20 years and 50+ production applications. Ready for the next 20.