Building KOS applications requires a structured development approach that bridges device communication, business logic, and user interface concerns. The KOS development workflow establishes clear phases and architectural boundaries that enable teams to build sophisticated device applications efficiently while maintaining code quality and testability.
The Model-First Development Approach
KOS development begins with modeling device capabilities rather than designing user interfaces. This approach ensures that applications accurately represent physical device constraints and operational requirements before considering how users will interact with those capabilities.
The workflow starts with understanding device communication patterns—what topics devices publish, what commands they accept, and what operational states they maintain. From this foundation, developers create models that mirror device behavior while abstracting system complexity through clean TypeScript interfaces.
This model-first approach prevents common pitfalls where user interface assumptions drive device integration decisions, leading to applications that feel natural to users while remaining technically sound from a hardware perspective.
Scaffolding and Code Generation
The KOS CLI provides sophisticated scaffolding that generates properly structured boilerplate code following framework conventions. Rather than manually creating model files and registration patterns, developers use targeted commands to establish correct architectural foundations:
# Create a basic device model
kosui model --name temperature-sensor --project device-models
# Generate singleton model for system-wide concerns
kosui model --name system-status --project device-models --singleton
# Create container model for managing collections
kosui model --name sensor-array --project device-models --containerThis scaffolding creates complete model structures including implementation files, registration modules, and TypeScript type definitions. The generated code follows KOS conventions while providing clear extension points for device-specific functionality.
The CLI approach accelerates initial development while ensuring consistency across team members and projects. New developers can begin implementing device logic immediately rather than learning framework registration patterns and file organization conventions.
Event-Driven Integration Development
Once models are scaffolded, development focuses on connecting them to device event streams. This phase requires understanding the device communication patterns established during system architecture—which topics carry operational state, which endpoints accept commands, and how device errors are communicated.
Event handlers form the primary bridge between physical devices and model state. The development process involves identifying relevant device topics and implementing handlers that maintain model consistency:
@kosModel({ modelTypeId: "environmental-monitor", singleton: false })
export class EnvironmentalMonitorImpl implements IKosDataModel {
temperature?: number;
humidity?: number;
airQuality?: number;
status: "normal" | "alert" | "critical" = "normal";
@kosTopicHandler({
topic: "/sensors/environment/temperature",
websocket: true,
})
handleTemperatureUpdate(reading: TemperatureReading): void {
this.temperature = reading.celsius;
this.evaluateEnvironmentalStatus();
}
@kosTopicHandler({
topic: "/sensors/environment/humidity",
websocket: true,
})
handleHumidityUpdate(reading: HumidityReading): void {
this.humidity = reading.percentage;
this.evaluateEnvironmentalStatus();
}
private evaluateEnvironmentalStatus(): void {
if (this.temperature && this.temperature > 85) {
this.status = "critical";
this.triggerTemperatureAlert();
} else if (this.humidity && this.humidity > 80) {
this.status = "alert";
} else {
this.status = "normal";
}
}
}This event-driven development ensures that models remain synchronized with device state while implementing business logic that responds appropriately to device conditions.
Relationship Architecture and Dependencies
KOS applications typically involve multiple devices that must coordinate their operations. The development workflow includes establishing model relationships that mirror physical system architecture while providing clean programmatic interfaces for coordination.
The framework provides two primary relationship patterns: containment for representing physical hierarchies and dependencies for service relationships. Developers choose appropriate patterns based on the underlying device architecture:
@kosModel({ modelTypeId: "cooling-system", singleton: false })
export class CoolingSystemImpl implements IKosDataModel {
// Containment for physical components
@kosChild compressor?: CompressorModel;
@kosChild condenser?: CondenserModel;
@kosChild evaporator?: EvaporatorModel;
// Dependencies for shared services
@kosDependency({
modelType: "temperature-control-service",
lifecycle: DependencyLifecycle.LOAD,
})
temperatureService!: TemperatureControlService;
async init(): Promise<void> {
// Initialize physical components
this.compressor = Compressor.instance("main-compressor").build();
this.condenser = Condenser.instance("main-condenser").build();
this.evaporator = Evaporator.instance("main-evaporator").build();
}
@kosModelEffect({
dependencies: (model) => [
model.compressor?.status,
model.condenser?.temperature,
model.evaporator?.airflow
],
})
coordinateSystemOperation(): void {
// System-level coordination logic
if (this.compressor?.status === "running" && this.condenser?.temperature) {
if (this.condenser.temperature > 120) {
this.temperatureService.requestCoolingAssistance();
}
}
}
}This relationship architecture enables complex device systems while maintaining clear separation between individual device concerns and system-level coordination logic.
Asynchronous Operations and Future Management
Device operations often require extended time periods—calibration procedures, dispensing operations, or system diagnostics. The development workflow incorporates KOS Futures to manage these long-running operations while providing user feedback and cancellation capabilities:
@kosModel({ modelTypeId: "beverage-dispenser", singleton: false })
export class BeverageDispenserImpl implements IKosDataModel, FutureContainer {
@kosFuture()
async performSystemCalibration(): Promise<CalibrationResult> {
return this.calibrationService.calibrateAll(this.deviceId);
}
@kosFuture({ alias: "DISPENSE" })
async dispenseProduct(productId: string, volume: number): Promise<DispenseResult> {
if (!this.validateDispenseRequest(productId, volume)) {
throw new Error("Invalid dispense parameters");
}
return this.dispensingService.dispense({
deviceId: this.deviceId,
productId,
volume,
priority: "standard"
});
}
onFutureUpdate(future: IFutureModel): void {
if (future.alias === "DISPENSE") {
if (future.endState && future.success) {
this.recordSuccessfulDispense(future.result as DispenseResult);
} else if (future.error) {
this.handleDispenseFailure(future.error);
}
}
}
private validateDispenseRequest(productId: string, volume: number): boolean {
const product = this.availableProducts.find(p => p.id === productId);
return product !== undefined &&
volume > 0 &&
volume <= product.maxVolume &&
this.deviceStatus === "ready";
}
}Future integration enables sophisticated progress tracking and error handling while maintaining clean separation between device operations and user interface feedback mechanisms.
React Integration and Hook Generation
The development workflow transitions from device modeling to user interface creation through generated React hooks that provide type-safe model access with automatic lifecycle management:
# Generate React hooks for device models
kosui hook --modelName temperature-sensor --project ui-components
kosui hook --modelName cooling-system --project ui-componentsGenerated hooks handle model instantiation, loading states, and cleanup while exposing model properties and methods through familiar React patterns:
export const EnvironmentalDashboard: React.FC = () => {
const {
model: monitor,
status,
KosModelLoader
} = useEnvironmentalMonitor("main-building");
return (
<KosModelLoader {...status}>
<div className="environmental-dashboard">
<StatusPanel status={monitor.status} />
<MetricsGrid>
<MetricCard
title="Temperature"
value={monitor.temperature}
unit="°F"
alert={monitor.temperature && monitor.temperature > 85}
/>
<MetricCard
title="Humidity"
value={monitor.humidity}
unit="%"
alert={monitor.humidity && monitor.humidity > 80}
/>
<MetricCard
title="Air Quality"
value={monitor.airQuality}
unit="AQI"
alert={monitor.airQuality && monitor.airQuality > 150}
/>
</MetricsGrid>
{monitor.status !== "normal" && (
<AlertBanner
severity={monitor.status}
message="Environmental conditions require attention"
onAcknowledge={() => monitor.acknowledgeAlert()}
/>
)}
</div>
</KosModelLoader>
);
};This integration approach maintains clear separation between device logic and presentation concerns while ensuring that user interfaces remain synchronized with device state through reactive model properties.
Testing Strategy Throughout Development
KOS applications require testing at multiple levels—device communication verification, business logic validation, and user interface functionality. The development workflow incorporates testing patterns that align with the architectural separation of concerns:
// Model-level testing focuses on device logic
describe("EnvironmentalMonitor", () => {
let monitor: EnvironmentalMonitorModel;
beforeEach(() => {
monitor = EnvironmentalMonitor.instance("test-monitor").build();
});
it("should update status when temperature exceeds threshold", () => {
monitor.handleTemperatureUpdate({ celsius: 90, timestamp: new Date() });
expect(monitor.temperature).toBe(90);
expect(monitor.status).toBe("critical");
});
it("should coordinate alerts across multiple sensors", () => {
const alertSpy = jest.spyOn(monitor, 'triggerTemperatureAlert');
monitor.handleTemperatureUpdate({ celsius: 85, timestamp: new Date() });
monitor.handleHumidityUpdate({ percentage: 85, timestamp: new Date() });
expect(alertSpy).toHaveBeenCalled();
expect(monitor.status).toBe("critical");
});
});
// Integration testing verifies model coordination
describe("CoolingSystemIntegration", () => {
it("should coordinate compressor and condenser operations", async () => {
const system = CoolingSystem.instance("test-system").build();
await system.init();
// Simulate high condenser temperature
system.condenser!.temperature = 125;
// Verify system coordination
const assistanceRequest = jest.spyOn(system.temperatureService, 'requestCoolingAssistance');
system.coordinateSystemOperation();
expect(assistanceRequest).toHaveBeenCalled();
});
});Component testing focuses on user interaction and visual behavior while using mock models to isolate presentation concerns from device complexity.
Performance Optimization and Production Preparation
The development workflow includes performance considerations that ensure applications remain responsive under various operational conditions. This involves monitoring high-frequency device events, optimizing model update patterns, and implementing efficient error handling:
const PROP_SENSOR_ID = createPropKey<HighFrequencySensorImpl>("sensorId");
@kosModel({ modelTypeId: "high-frequency-sensor", singleton: false })
export class HighFrequencySensorImpl implements IKosDataModel {
sensorId!: string;
private updateBuffer: SensorReading[] = [];
private lastUpdate: number = 0;
@kosTopicHandler({
topic: `/sensors/high-frequency/${PROP_SENSOR_ID}/data`,
websocket: true
})
handleHighFrequencyData(reading: SensorReading): void {
// Buffer high-frequency updates for performance
this.updateBuffer.push(reading);
const now = performance.now();
if (now - this.lastUpdate > 100) { // Update UI at most every 100ms
this.processBufferedUpdates();
this.lastUpdate = now;
}
}
private processBufferedUpdates(): void {
if (this.updateBuffer.length === 0) return;
// Process most recent reading
const latestReading = this.updateBuffer[this.updateBuffer.length - 1];
this.currentValue = latestReading.value;
// Calculate aggregates from buffer
this.averageValue = this.calculateAverage(this.updateBuffer);
this.maxValue = Math.max(...this.updateBuffer.map(r => r.value));
this.updateBuffer = [];
}
}Production preparation includes deployment configuration, error monitoring setup, and performance baseline establishment to ensure reliable operation in device environments.
Architecture Benefits and Scalability
The KOS development workflow creates applications with clear architectural boundaries that scale effectively as system complexity grows. Device models remain focused on device-specific concerns, business models handle coordination and operational logic, and presentation components provide user interfaces without duplicating device communication code.
This separation enables teams to work in parallel—device experts can focus on accurate hardware modeling while user experience designers create interfaces that surface device capabilities effectively. The resulting applications maintain consistency between physical device behavior and user interface feedback while remaining maintainable as device capabilities evolve.
The workflow’s emphasis on testing at appropriate architectural levels ensures that applications remain reliable while providing confidence that device integration, business logic, and user interface concerns all function correctly both individually and as an integrated system.
The KOS development workflow transforms the complex challenge of device application development into a structured, predictable process that produces reliable, maintainable applications capable of managing sophisticated device operations while providing excellent user experiences.
