State management in KOS applications represents a fundamental departure from traditional web development paradigms. Rather than managing distributed client state across multiple layers and synchronization points, KOS establishes physical devices as the authoritative source of truth, creating applications that mirror device reality rather than managing independent application state.
The Paradigm Shift: From State Management to State Reflection
Traditional web applications face the complex challenge of maintaining consistency across distributed state layers—from component local state through global application state to server persistence. This distribution creates synchronization challenges, cache invalidation complexity, and the constant need to coordinate between different sources of truth.
KOS applications eliminate this complexity by recognizing that in device-centric systems, physical reality defines what the state actually is. A temperature sensor reading represents the actual temperature, a door position sensor reflects the actual door state, and ingredient level sensors report true ingredient amounts. The application’s role shifts from managing state to accurately reflecting the authoritative state that already exists in the physical world:
// Traditional approach: Application manages state
const [temperature, setTemperature] = useState(20);
const [isHeating, setIsHeating] = useState(false);
const handleHeatUp = () => {
setIsHeating(true);
setTemperature(temp => temp + 1);
// State created locally, may not reflect reality
};
// KOS approach: Application reflects device state
@kosModel({ modelTypeId: "heater-model", singleton: false })
export class HeaterModelImpl implements IKosDataModel {
currentTemperature?: number; // From actual temperature sensor
isHeating?: boolean; // From actual heater controller
// User interaction sends command to device
startHeating(): Future<void> {
return this.sendCommand('/heater/start');
// State updates come from device response, not local changes
}
@kosTopicHandler({ topic: '/sensors/temperature' })
handleTemperatureUpdate(temp: number): void {
this.currentTemperature = temp; // Reflects actual device state
}
}This architectural difference creates profound implications for how developers think about and implement state throughout their applications.
Unidirectional State Flow from Physical Devices
The flow of state in KOS applications follows a clear unidirectional pattern that eliminates the bidirectional synchronization challenges common in traditional applications. State changes originate exclusively from physical devices and flow through backend systems to update models and ultimately user interfaces:
Physical Device → Backend → WebSocket → Model → UI Component
↑
User Interaction → CommandThis unidirectional flow ensures that the UI always reflects actual device state rather than assumptions or local modifications. When users interact with the interface, they send commands to devices rather than updating local state. The resulting state changes then flow back through the established pipeline, ensuring that what users see represents confirmed device reality.
This pattern eliminates common web application challenges such as stale data problems, optimistic update rollbacks, and race conditions between client updates. Users never see impossible device states because the interface only displays confirmed device status.
State Ownership and Responsibility Boundaries
In KOS applications, ownership boundaries are clearly defined by physical reality. Devices own their own state—temperature sensors own temperature readings, door controllers own door positions, dispensers own pour status—while models serve as subscribers to device state changes rather than independent state containers.
This ownership model creates clean separation between device-related state and UI-related state:
@kosModel({ modelTypeId: "beverage-dispenser", singleton: false })
export class BeverageDispenserImpl implements IKosDataModel {
// Device state - managed by physical hardware
currentTemperature?: number;
pourStatus?: "idle" | "pouring" | "complete";
ingredientLevels?: Record<string, number>;
// These values come from device sensors and controllers
@kosTopicHandler({ topic: '/dispenser/status' })
handleStatusUpdate(status: DispenserStatus): void {
this.currentTemperature = status.temperature;
this.pourStatus = status.pourState;
this.ingredientLevels = status.ingredients;
}
}
// UI components handle their own presentation state
function DispenserInterface() {
const dispenser = useKosModel(BeverageDispenser);
// UI state - managed by React
const [showInstructions, setShowInstructions] = useState(false);
const [animationState, setAnimationState] = useState<"enter" | "exit">("enter");
// Device state comes from model, UI state managed locally
return (
<div>
<Temperature value={dispenser.currentTemperature} />
<PourStatus status={dispenser.pourStatus} />
{showInstructions && <Instructions />}
</div>
);
}This clear separation enables developers to focus on modeling accurate device behavior while maintaining the flexibility to manage presentation concerns through familiar React patterns.
Reactive State Propagation with Model Effects
KOS provides sophisticated mechanisms for handling side effects when model state changes through the @kosModelEffect decorator. This system enables reactive programming patterns where external systems automatically respond to device state changes without manual coordination:
@kosModel({ modelTypeId: "climate-control", singleton: false })
export class ClimateControlImpl implements IKosDataModel {
currentTemperature?: number;
targetTemperature: number = 72;
isOnline?: boolean;
alertsSent: string[] = [];
// Reactive effect responds to temperature and connectivity changes
@kosModelEffect({
dependencies: (model) => [model.currentTemperature, model.isOnline],
})
handleTemperatureAlerts(): void {
if (this.isOnline && this.currentTemperature && this.currentTemperature > 85) {
this.sendTemperatureAlert();
}
}
// Coordinate with external systems when device state changes
@kosModelEffect({
dependencies: (model) => [model.targetTemperature],
})
updateDisplaySettings(): void {
if (this.targetTemperature) {
this.displayController.updateTarget(this.targetTemperature);
this.loggingService.recordTargetChange(this.targetTemperature);
}
}
private sendTemperatureAlert(): void {
const alertId = `temp-${Date.now()}`;
this.alertsSent.push(alertId);
this.notificationService.sendAlert({
type: "high-temperature",
value: this.currentTemperature,
timestamp: new Date()
});
}
}Model effects provide a declarative way to specify what should happen when device state changes, enabling complex reactive behaviors while maintaining clear dependency relationships and avoiding the callback complexity common in event-driven systems.
Dynamic Configuration with Property Keys
KOS’s PropKey system enables models to use their own property values to dynamically configure dependencies, topic handlers, and other model metadata. This capability supports flexible architectures where model behavior adapts based on runtime configuration:
interface DeviceControllerOptions {
controllerId: string;
deviceType: 'dispenser' | 'cooler' | 'sensor';
location: string;
}
const PROP_CONTROLLER_ID = createPropKey<DeviceController>("controllerId");
const PROP_DEVICE_TYPE = createPropKey<DeviceController>("deviceType");
const PROP_LOCATION = createPropKey<DeviceController>("location");
@kosModel({ modelTypeId: "device-controller", singleton: false })
export class DeviceControllerImpl implements IKosDataModel {
controllerId: string;
deviceType: 'dispenser' | 'cooler' | 'sensor';
location: string;
// Dynamic dependency resolution based on device type
@kosDependency({
modelType: `${PROP_DEVICE_TYPE}-model`,
id: `${PROP_DEVICE_TYPE}-${PROP_CONTROLLER_ID}`,
options: {
controllerId: PROP_CONTROLLER_ID,
location: PROP_LOCATION
}
})
device!: DispenserModel | CoolerModel | SensorModel;
// Device-specific topic subscriptions
@kosTopicHandler({
topic: `/devices/${PROP_DEVICE_TYPE}/${PROP_CONTROLLER_ID}/status`,
websocket: true
})
handleDeviceStatus(payload: DeviceStatusPayload): void {
this.logger.info(`Status for ${this.deviceType} ${this.controllerId}: ${payload.status}`);
this.processStatusUpdate(payload);
}
constructor(modelId: string, options: DeviceControllerOptions) {
this.controllerId = options.controllerId;
this.deviceType = options.deviceType;
this.location = options.location;
}
}PropKeys enable highly reusable model configurations that adapt to different deployment scenarios while maintaining type safety and clear dependency relationships.
State Consistency and Real-Time Accuracy
The KOS state management philosophy provides strong consistency guarantees that traditional applications struggle to achieve. Because device state is authoritative and all clients receive updates through the same WebSocket channels, multiple clients always see identical device status without manual refresh requirements.
This consistency extends to temporal accuracy—the UI reflects actual device state at the current moment rather than displaying cached or assumed values. When a device changes state, all connected clients receive updates automatically, ensuring that critical device status is always current across all interfaces.
The absence of optimistic updates in device-related state eliminates scenarios where users might see impossible device states or need to handle rollback situations. The interface only displays confirmed device status, creating more reliable and trustworthy user experiences.
State Testing and Debugging Simplification
KOS’s state management approach significantly simplifies testing and debugging by eliminating the complex state synchronization scenarios that plague traditional applications. Testing focuses on verifying that models accurately reflect device state changes and that commands properly reach device endpoints:
describe("ClimateControlModel", () => {
it("should reflect temperature changes from device", async () => {
const model = ClimateControl.instance("test").build();
// Simulate device temperature update
EventBus.publish("/sensors/temperature", { temperature: 75 });
// Verify model reflects device state
expect(model.currentTemperature).toBe(75);
});
it("should send commands when target changes", async () => {
const model = ClimateControl.instance("test").build();
const commandSpy = jest.spyOn(model, "sendCommand");
// Change target temperature
model.setTargetTemperature(68);
// Verify command sent to device
expect(commandSpy).toHaveBeenCalledWith("/climate/set-target", { temperature: 68 });
});
});This testing approach focuses on the essential behaviors—accurate state reflection and reliable command execution—rather than complex state synchronization scenarios, making tests more focused and maintainable.
The Mental Model Transformation
Adopting KOS’s state management philosophy requires a fundamental shift in how developers think about application state. Instead of asking “Where should this state live?” or “How do I sync between components?”, developers learn to ask “What device state does this represent?” and “How does the device report changes?”
This transformation leads to applications that feel more responsive and trustworthy because they accurately represent physical reality rather than managing independent application assumptions. Users interact with actual device capabilities rather than interface abstractions, creating more intuitive and reliable experiences.
The result is a simplified state architecture that eliminates the complexity of distributed state management by recognizing physical devices as natural owners of their own state. The UI becomes a window into device state rather than a manager of application state, leading to more maintainable, reliable, and accurate device interfaces.
KOS’s state management philosophy demonstrates that by aligning software architecture with physical reality, applications can achieve both greater simplicity and higher reliability while providing users with real-time, accurate views of the devices they’re interacting with.
