Skip to main content
The KDS Frontend follows a clean architecture pattern with clear separation of concerns across four distinct layers. This architecture ensures maintainability, testability, and scalability of the codebase.

Layered Architecture

The application is organized into the following layers:

Domain Layer

Core business logic and rules

Application Layer

Application services and interfaces

Infrastructure Layer

External integrations (HTTP, WebSocket)

Presentation Layer

UI components and contexts

Directory Structure

source/
├── domain/                    # Domain Layer
│   └── order/
│       ├── order-status.ts
│       └── order-transitions.ts
├── application/               # Application Layer
│   ├── order/
│   │   ├── order-repository.ts
│   │   ├── order-realtime.ts
│   │   └── order-events.ts
│   ├── hook/
│   │   └── use-order-detail.ts
│   └── shared/
│       └── http-client.ts
├── infraestructure/          # Infrastructure Layer
│   ├── http/
│   │   ├── order.repository.ts
│   │   └── axios-http.client.ts
│   └── socket/
│       └── order-realtime.socket.ts
├── orchestrators/            # Orchestration Layer
│   └── order/
│       └── OrderOrchestrator.ts
├── components/               # Presentation Layer
│   ├── Kanban/
│   ├── Column/
│   └── card/
├── contexts/                 # State Management
│   └── Orders.context.tsx
├── dtos/                     # Data Transfer Objects
│   ├── OrderList.dto.ts
│   └── OrderDetails.dto.ts
└── pages/                    # Next.js Pages

Layer Details

Domain Layer

The domain layer contains pure business logic with no external dependencies. It defines the core rules and types of the application.Location: domain/Key Responsibilities:
  • Define domain types (e.g., OrderStatus)
  • Implement business rules (e.g., canTransition)
  • No dependencies on other layers
Example: Order Status Transitions
domain/order/order-transitions.ts
import { OrderStatus } from "./order-status";

const ALLOWED_TRANSITIONS: Record<OrderStatus, OrderStatus[]> = {
  RECEIVED: ["CONFIRMED", "CANCELLED"],
  CONFIRMED: ["PREPARING", "CANCELLED"],
  PREPARING: ["READY", "CANCELLED"],
  READY: ["PICKED_UP", "CANCELLED"],
  PICKED_UP: ["DELIVERED"],
  DELIVERED: [],
  CANCELLED: [],
};

export function canTransition(from: OrderStatus, to: OrderStatus): boolean {
  return ALLOWED_TRANSITIONS[from].includes(to);
}
The domain layer ensures business rules are enforced consistently across the entire application.

Data Flow

1

User Interaction

User drags an order card to a new column in the Kanban board
2

Domain Validation

The canTransition function validates if the status change is allowed
if (!canTransition(current.status, targetStatus)) return;
3

Orchestrator Coordination

The context calls the orchestrator to update the order status
await orderOrchestrator.updateOrderState(id, status);
4

Infrastructure Execution

The HTTP repository sends a PATCH request to the backend API
await this.http.patch(`/api/v1/orders/${id}/status`, { toStatus });
5

Real-time Broadcast

The backend broadcasts the update via WebSocket to all connected clients
6

UI Update

The socket handler triggers the onUpdated event, and the context updates the UI
this.socket.on("order.status.updated", (order: OrderListDto) => {
  events.onUpdated?.(order);
});

Benefits of This Architecture

Testability

Each layer can be tested in isolation with mocked dependencies

Maintainability

Clear separation makes it easy to locate and modify code

Flexibility

Swap implementations (e.g., replace HTTP with GraphQL) without changing business logic

Scalability

Add new features by following established patterns

Next Steps

Order Status

Learn about order status types and transition rules

Real-time Sync

Understand how WebSocket synchronization works

Build docs developers (and LLMs) love