Classes and interfaces#
Classes and interfaces model reusable contracts and implementation objects. Use them for program structure and service internals; keep tenant-configurable policy in tables and resolver functions.
Classes#
Class declarations support annotations, access modifiers, abstract and virtual modifiers, optional inheritance, fields, methods, constructors, properties, table bindings, UI layout, nested classes, validation, and business rules.
@audited
public abstract class InvoiceProcessor inherits BaseProcessor {
private field repository: InvoiceRepository;
property name: string;
InvoiceProcessor(repo: InvoiceRepository) {
repository = repo;
}
public async function approve(invoice_id: InvoiceId): bool {
let invoice = repository.get(invoice_id);
return invoice is not null;
}
}A class may inherit from one or more type expressions.
class PostingProcessor inherits BaseProcessor, AuditableProcessor {
amount: decimal required;
field currency: CurrencyCode required;
}The field keyword is optional for class field declarations inside classes.
Class fields#
Class fields use the same type expression and field modifier style as other member declarations.
class CacheState {
field cache_key: string required;
private field retry_count: int default(0);
}Methods#
Methods use function-style parameters and return types. Bodies are either block bodies or semicolon declarations.
class InvoiceCalculator {
function calculate_total(invoice: Invoice): decimal {
return invoice.net_amount + invoice.tax_amount;
}
abstract function resolve_policy(company_code: CompanyCode): PostingPolicy?;
function load(invoice_id: InvoiceId): async Invoice?;
}Method return types can be prefixed with async.
Constructors#
Constructor declarations use the class constructor name, parameters, and a block body.
class InvoiceProcessor {
field repository: InvoiceRepository;
InvoiceProcessor(repo: InvoiceRepository) {
repository = repo;
}
}Properties#
Properties define typed members without implementation bodies.
class InvoiceView {
property display_name: string;
property active: bool;
}Table bindings#
Classes can bind to tables and provide options.
class InvoiceView {
bind table Invoice as invoices with {
mode: "readwrite";
cache: true;
};
}Binding options are key-value expressions. Use them for implementation metadata such as mode, cache behavior, or adapter hints, not for tenant-owned policy values.
Class UI#
Classes can provide ui: using either a normal expression or a source UI layout block.
class InvoiceView {
field status: string;
field amount: decimal;
ui: {
group "Invoice" {
columns: 2;
row {
field status;
field amount;
}
}
}
}For the full layout grammar, see Source UI layouts.
Nested classes#
Classes can contain nested class declarations for helper implementation types.
class InvoiceProcessor {
private class RetryState {
field attempts: int default(0);
}
}Use nested classes for local implementation structure. Shared contracts should move to top-level classes or interfaces.
Class validation#
Class validation sections contain named validation expressions or references to reusable validation rules.
class InvoiceView {
validation {
valid_state: status is not null;
allowed_transition: rule TransitionAllowed(status, proposed_status);
}
}Validation rule references may be written with or without arguments.
class InvoiceView {
validation {
configured: rule PostingConfigured;
transition_allowed: rule TransitionAllowed(current_status, next_status);
}
}Class business rules#
Class business rules use business_rules: and named rule blocks.
class InvoicePolicy {
business_rules: {
rule "approval required": {
condition: amount > threshold;
rule approval_route_required(company_code);
};
}
}Interfaces#
Interfaces define signatures only. Interface members include functions, properties, constants, and annotations.
interface Approver {
function approve(invoice_id: InvoiceId): bool;
function resolve(invoice_id: InvoiceId) -> async ApprovalDecision;
property name: string;
const version: string = "1";
}Interface functions can use : or -> for return types and may include function modifiers.
Interface parameters can include default expressions, and interface constants require literal values.
interface Exporter {
async function export(company_code: CompanyCode, include_closed: bool = false) -> async Json;
property format: string;
const default_format: string = "json";
}Interfaces may also contain annotation usages as members or on member signatures.
interface Exporter {
@Adapter("json")
function export(company_code: CompanyCode): Json;
}Implementation boundary#
Use interfaces for stable contracts between services or runtime adapters. Use classes for implementation details. Do not use either to hide configurable business policy that should be visible as table data.