
Codion Desktop
Application Framework
Observable Models
public final class CandidateModel {
private final FilterTableModel<CandidateRow, CandidateColumn> tableModel;
private final State installedOnly;
public CandidateModel() {
tableModel = FilterTableModel.builder()
.columns(new CandidateTableColumns())
.items(new CandidateItems())
.included(new CandidateIncluded())
.build()
installedOnly = State.builder()
.listener(tableModel.items()::filter)
.build()
}
public FilterTableModel<CandidateRow, CandidateColumn> tableModel() {
return tableModel;
}
public State installedOnly() {
return installedOnly;
}
}
Reactive Components
public final class CandidatePanel extends JPanel {
private final FilterTable<CandidateRow, CandidateColumn> table;
private final JCheckBox installedOnly;
public CandidatePanel(CandidateModel candidateModel, ObservableState installing) {
table = FilterTable.builder()
.model(candidateModel.tableModel())
.sortable(false)
.enabled(installing.not())
.cellRenderer(CandidateColumn.INSTALLED,
FilterTableCellRenderer.builder()
.columnClass(Integer.class)
.horizontalAlignment(CENTER)
.build())
.build();
installedOnly = checkBox()
.link(candidateModel.installedOnly())
.text("Installed")
.mnemonic('T')
.enabled(installing.not())
.build();
}
}
Type-Safe Domain Modeling
// Domain API
interface Country {
EntityType TYPE = DOMAIN.entityType("world.country");
Column<String> CODE = TYPE.stringColumn("code");
Column<String> NAME = TYPE.stringColumn("name");
Column<Integer> POPULATION = TYPE.integerColumn("population");
Column<Integer> CAPITAL_ID = TYPE.integerColumn("capital_id");
ForeignKey CAPITAL_FK = TYPE.foreignKey("capital_fk", CAPITAL_ID, City.ID);
}
// Domain Implementation
EntityDefinition country() {
return Country.TYPE.define(
Country.CODE.define()
.primaryKey()
.caption("Code")
.maximumLength(3),
Country.NAME.define()
.column()
.caption("Name")
.nullable(false)
.maximumLength(52),
Country.POPULATION.define()
.column()
.caption("Population")
.nullable(false),
Country.CAPITAL_ID.define()
.column()
.nullable(false),
Country.CAPITAL_FK.define()
.foreignKey()
.caption("Capital"))
.caption("Country")
.build();
}
Declarative UI Building
// UI Component Creation & Layout
@Override
protected void initializeUI() {
// Create typed components
createTextField(Country.CODE)
.columns(3)
.upperCase(true);
createTextField(Country.NAME)
.columns(20);
createIntegerField(Country.POPULATION)
.columns(10)
.minimum(0)
.nullable(false);
createComboBoxPanel(Country.CAPITAL_FK, this::createCapitalEditPanel)
.includeAddButton(true)
.includeEditButton(true)
.preferredComboBoxWidth(180);
setLayout(flexibleGridLayout(3, 1));
// Layout with input panels
add(borderLayoutPanel()
.west(createInputPanel(Country.CODE))
.center(createInputPanel(Country.NAME)));
addInputPanel(Country.POPULATION);
addInputPanel(Country.CAPITAL_FK);
}
// Complete Application Bootstrap
EntityApplication.builder(WorldAppModel.class, WorldAppPanel.class)
.domain(World.DOMAIN)
.version(WorldAppModel.VERSION)
.icon(WorldAppPanel.ICON)
.startupDialog(false)
.defaultLookAndFeel(MonokaiPro.class)
.defaultUser(User.parse("scott:tiger"))
.start();
Key Features
π₯οΈ Desktop First
When web deployment isn't required, desktop applications can provide responsive interfaces without browser constraints.
Native Desktop Advantages
β’ Keyboard navigation throughout with plenty of shortcuts
β’ System integration with file dialogs, clipboard, and desktop features
β’ True multitasking without browser tab limitations
β’ Lower memory usage compared to web applications
β’ Offline capability with local data and no network dependencies
Desktop UI Components
Tables, forms, and controls that handle large datasets efficiently. Context menus, drag-and-drop, focus management, and accessibility features that work across platforms.
Modern Themes
Include ~50 FlatLAF Look and Feels. Users can dynamically switch between Material themes (Darker, Oceanic, Night Owl), developer favorites (Dracula, One Dark, Gruvbox), or nature-inspired themes (Aurora Borealis, Sakura) without restarting (View β Select Look & Feel).
// Include the 6 core Flat Look and Feels
<dependency>
<groupId>is.codion</groupId>
<artifactId>codion-plugin-flatlaf</artifactId>
</dependency>
// Include ~50 IntelliJ theme based Flat Look and Feels
<dependency>
<groupId>is.codion</groupId>
<artifactId>codion-plugin-flatlaf-intellij-themes</artifactId>
</dependency>
π Type Safe
Every aspect of Codion is compile-time checked. No string-based column references, no runtime surprises. Foreign keys are first-class citizens with automatic loading.
// Compile-time safety everywhere
Entity country = connection.selectSingle(Country.CODE.equalTo("USA"));
String capitalName = country.get(Country.CAPITAL_FK).get(City.NAME);
// Query results are type-checked at compile time
List<String> artistNames = connection.select(Artist.NAME, Artist.ID.in(1, 2));
// Generic column definitions with type safety
Column<List<String>> TAGS = TYPE.column("tags", new TypeReference<>() {});
Column<Location> COORDINATES = TYPE.column("location", Location.class);
// Conditions enforce correct attribute types
Condition lowRated = Track.RATING.lessThanOrEqualTo(4);
Condition expensive = Track.UNITPRICE.greaterThan(BigDecimal.ONE);
π Observable by Design
Everything in Codion is observable. UI components automatically update when data changes. No manual synchronization, no event bus complexity. Just declare relationships and watch them work.
// Traditional approach - manual synchronization
textField.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) { updateFilter(); }
public void removeUpdate(DocumentEvent e) { updateFilter(); }
public void insertUpdate(DocumentEvent e) { updateFilter(); }
});
button.setEnabled(false); // Remember to update this everywhere!
// Codion approach - synchronous observable binding
Value<String> filter = Value.nullable();
State canSubmit = State.and(hasData, isValid);
JTextField filterField = Components.stringField()
.link(filter)
.hint("Filter...")
.build();
JButton submitButton = Components.button()
.action(submitAction)
.enabled(canSubmit) // Updates automatically and synchronously!
.build();
π Reduces Complexity
Adding ONE detail table causes 61-109% code explosions in other frameworks. That's just basic CRUD - for business-grade features (sortable columns, keyboard navigation, export, validation indicators), other frameworks need 10-15x more code.
// Basic master-detail
React + Node.js: 515 lines
Spring Boot: 355 lines
Codion: 143 lines (with enterprise features included)
// For feature parity with Codion's defaults:
React + Node.js: ~1,915 lines (+1,400 for table features)
Spring Boot: ~1,355 lines (+1,000 for UI features)
Codion: 143 lines (no additional code needed)
βοΈ Battle tested
Codion has powered 50+ production applications over 20 years across scientific and government institutions, including critical infrastructure systems.
πͺΆ Lightweight
Codion is built on Java Standard Edition - Swing, JDBC, and RMI. No third-party application servers or complex deployment scenarios.
Resource Efficiency
Codion applications typically use fewer resources than Electron-based counterparts:
// Typical resource usage comparison:
Electron app: 400MB+ RAM, 150MB+ disk
Codion app: 40-80MB RAM, 15MB disk
// CPU efficiency:
Electron: Constant background activity
Codion: Near-zero when idle
π€ AI/LLM Ready
Codion's parameterized builder APIs enable code generation. Every method takes explicit parameters, making it suitable for AI-assisted development.
// LLM-friendly APIs
Components.stringField()
.link(nameValue)
.columns(20)
.upperCase(true)
.maximumLength(20)
.action(insertAction)
.horizontalAlignment(CENTER)
.selectAllOnFocusGained(true)
.transferFocusOnEnter(true);
ποΈ Domain-Driven Design
Your business domain becomes executable, type-safe code. Define entities once with rich metadata, and Codion generates all CRUD operations, validations, and UI components automatically.
// Define your domain - this is all you need!
interface Customer {
EntityType TYPE = DOMAIN.entityType("store.customer");
Column<String> NAME = TYPE.stringColumn("name");
Column<String> EMAIL = TYPE.stringColumn("email");
Column<BigDecimal> CREDIT_LIMIT = TYPE.bigDecimalColumn("credit_limit");
}
// This definition provides:
// β Type-safe CRUD operations
// β Form validation and UI components
// β Search and filter capabilities
// β Foreign key navigation
// Use it everywhere with compile-time safety:
Entity.Key customerKey = connection.insert(
entities.entity(Customer.TYPE)
.with(Customer.NAME, "John Doe")
.with(Customer.EMAIL, "john@example.com")
.with(Customer.CREDIT_LIMIT, new BigDecimal(10_000))
.build());
π Rapid Development With Joy
Minimal boilerplate and configuration. From domain model to working CRUD application in hours. Clean APIs with IDE support and compile-time safety.
// Domain definitions for a couple of tables ~100 lines
// Initial application and UI setup ~150 lines
// Run application and start testing