Skip to content

Architecture Overview

Luca_Previ0o edited this page Dec 10, 2025 · 2 revisions

Architecture Overview

Java Server Interface is designed around a three-layer architectural model that prioritizes separation of concerns, modularity, and extensibility. Understanding this architecture is key to leveraging JSI's full potential.

The Three-Layer Model

┌─────────────────────────────────────────────────────────────┐
│  LAYER 3: Protocol Layer                                    │
│  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│  Application-specific logic                                 │
│  • HttpServer (routing, request parsing, response building) │
│  • DatabaseServer (query execution, storage management)     │
│  • Your custom protocol implementation                      │
├─────────────────────────────────────────────────────────────┤
│  LAYER 2: Transport Layer                                   │
│  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│  Connection management (protocol-agnostic)                  │
│  • ConnectionServer (TCP socket handling)                   │
│  • Thread-per-client model                                  │
│  • Generic I/O utilities (file reading)                     │
├─────────────────────────────────────────────────────────────┤
│  LAYER 1: Foundation Layer                                  │
│  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│  Core lifecycle and hooks                                   │
│  • Server (abstract base class)                             │
│  • start(), stop() lifecycle methods                        │
│  • onBeforeStart(), onServerStarted() hooks                 │
└─────────────────────────────────────────────────────────────┘

Design Philosophy: Optional Layers

The revolutionary aspect of JSI's architecture is that every layer is optional. You can:

  • Use all three layers (quickest path for standard protocols)
  • Skip Layer 2 and implement your own transport (e.g., UDP, WebSocket)
  • Skip Layer 3 and build completely custom protocols
  • Only use Layer 1 for maximum control

This flexibility means JSI adapts to your needs rather than forcing you into a specific architectural pattern.

Layer Breakdown

Layer 1: Foundation Layer

File: Server.java

The absolute minimum abstraction. This layer defines what any server must do, regardless of transport or protocol:

public abstract class Server {
    
    public abstract void start();
    
    protected void onBeforeStart() {}
    
    protected void onServerStarted() {}
    
    public abstract Response handleRequest(Request request);
}

Key Characteristics:

  • No assumptions about transport (could be TCP, UDP, shared memory, message queues)
  • No assumptions about protocol (could be HTTP, database queries, binary protocols)
  • Pure lifecycle management: start server, inject hooks, handle requests

When to use directly:

  • Building servers with non-standard transports (UDP, Unix sockets)
  • Maximum control over every aspect of server behavior
  • Educational purposes to understand server fundamentals

Example: See UDP chat server example

Layer 2: Transport Layer

File: connection/ConnectionServer.java

Adds TCP connection management on top of the foundation:

public abstract class ConnectionServer extends Server {
    
    private final int port;
    
    public void start() {
        // Creates ServerSocket, accepts connections
        // Spawns thread for each client
    }
    
    protected abstract Request parseRequest(String input);
    
    protected String readFile(String filePath) throws IOException {
        // Generic file I/O utility
    }
}

Key Characteristics:

  • Knows about sockets, nothing about protocols
  • Implements standard TCP server pattern (listen → accept → spawn thread)
  • Thread-per-client concurrency model
  • Provides utilities (file reading) useful for many protocols

Threading Model:

Server Thread          Client Thread 1         Client Thread 2
     │                        │                       │
     ├─ accept() ────────────►│                       │
     │                        ├─ parseRequest()       │
     │                        ├─ handleRequest()      │
     │                        └─ send response        │
     │                                                │
     ├─ accept() ────────────────────────────────────►│
     │                                                ├─ parseRequest()
     ├─ accept() (blocked)                            ├─ handleRequest()
     .                                                └─ send response
     .

Each client connection runs in its own thread, allowing concurrent request handling.

When to use directly:

  • Building custom TCP-based protocols
  • Need socket-level control but don't want to write accept loop
  • Custom request/response parsing logic

Layer 3: Protocol Layer

Files:

Implements specific application protocols on top of the transport layer:

HTTP Protocol Implementation

public abstract class HttpServer extends ConnectionServer {
    
    @Override
    protected HttpRequest parseRequest(String input) {
        // Parse HTTP request format
    }
    
    @Override
    public HttpResponse handleRequest(Request request) {
        // Route to appropriate handler method
    }
    
    protected void registerRoutes() {
        // Scan for @Route annotations
    }
}

Responsibilities:

  • HTTP request parsing (method, path, headers, parameters)
  • Annotation-based routing system
  • Response serialization (status line, headers, body)
  • Static file serving
  • Error handling (404, 500, etc.)

Database Protocol Implementation

public abstract class DatabaseServer extends ConnectionServer {
    
    private DatabaseEngine databaseEngine;
    
    @Override
    public QueryResult handleRequest(Request query) {
        return databaseEngine.execute((Query) query);
    }
}

Responsibilities:

  • Query parsing (delegated to specific implementations like MySQL)
  • Query execution through DatabaseEngine
  • Storage management through StorageEngine
  • Transaction support

When to use:

  • Building HTTP REST APIs or web servers
  • Creating database applications
  • Standard protocol implementations

Request/Response Abstraction

Throughout all layers, JSI uses a consistent Request/Response pattern:

Client                    Server
  │                         │
  ├──── Request ───────────►│
  │                         ├─ parseRequest()
  │                         ├─ handleRequest()
  │                         ├─ createResponse()
  │◄──── Response ──────────┤
  │                         │

Core Interfaces:

public interface Request {
    String serialize();
}

public interface Response {
    String serialize();
}

This abstraction allows any type of request/response to flow through the system:

  • HttpRequest / HttpResponse for HTTP protocol
  • Query / QueryResult for database protocol
  • Your custom request/response types

See Core Abstractions for detailed documentation.

SOLID Principles in Action

JSI's architecture exemplifies SOLID design principles:

1. Single Responsibility Principle (SRP)

Each layer has one reason to change:

  • Layer 1: Changes only if fundamental server lifecycle logic changes
  • Layer 2: Changes only if TCP socket handling strategy changes
  • Layer 3: Changes only if protocol specifications change

2. Open/Closed Principle (OCP)

Classes are open for extension, closed for modification:

  • Extend HttpServer to add new routes without modifying the base class
  • Implement StorageEngine to add new persistence backends
  • No need to modify existing code to add features

3. Liskov Substitution Principle (LSP)

Any ConnectionServer subclass can replace another:

ConnectionServer server = new HttpServer(8080);
// Can be replaced with:
ConnectionServer server = new DatabaseServer(3306, engine);
// Code using 'server' works identically

4. Interface Segregation Principle (ISP)

Small, focused interfaces instead of monolithic ones:

  • StorageEngine - only storage operations
  • DatabaseEngine - only query execution
  • Request / Response - only serialization

5. Dependency Inversion Principle (DIP)

High-level modules don't depend on low-level modules:

// High-level: DatabaseServer
// Low-level: JsonStorageEngine, XmlStorageEngine

// Both depend on abstraction: StorageEngine
DatabaseServer server = new DatabaseServer(port, 
    new DatabaseEngine(new JsonStorageEngine("./db")));

Data Flow Example

Let's trace an HTTP request through all layers:

1. Client sends HTTP request to localhost:8080
   GET /users?id=123 HTTP/1.1
   
   ▼
   
2. LAYER 2 (ConnectionServer)
   • ServerSocket.accept() receives connection
   • Spawns new thread for this client
   • Reads raw input stream into String
   
   ▼
   
3. LAYER 3 (HttpServer)
   • parseRequest(): Parses HTTP format
     → Creates HttpRequest object
     → Extracts path: "/users"
     → Extracts parameters: {"id": "123"}
   
   ▼
   
4. LAYER 3 (HttpServer - Routing)
   • handleRequest(): Matches path "/users"
   • Invokes @Route-annotated method
   • Method receives HttpRequest object
   
   ▼
   
5. APPLICATION LOGIC (Your Code)
   @Route(path = "/users")
   public HttpResponse getUser(HttpRequest req) {
       String id = req.getParameter("id");
       // ... business logic ...
       return createJsonResponse(OK, userData);
   }
   
   ▼
   
6. LAYER 3 (HttpServer - Response)
   • serialize(): Converts HttpResponse to string
     HTTP/1.1 200 OK
     Content-Type: application/json
     
     {"id": 123, "name": "Alice"}
   
   ▼
   
7. LAYER 2 (ConnectionServer)
   • Writes response to client socket
   • Closes connection
   • Thread terminates

Modularity Examples

Example 1: Swapping Storage Engines

// Start with JSON storage
StorageEngine storage = new JsonStorageEngine("./db");
DatabaseEngine engine = new DatabaseEngine(storage);

// Switch to XML storage - no other code changes!
StorageEngine storage = new XmlStorageEngine("./db");
DatabaseEngine engine = new DatabaseEngine(storage);

Example 2: Custom Transport Layer

// UDP-based server bypassing Layer 2
public class UdpChatServer extends Server {
    @Override
    public void start() {
        DatagramSocket socket = new DatagramSocket(port);
        // Custom UDP implementation
    }
}

Example 3: Hybrid Architecture

// Use Layer 2 but custom protocol (not HTTP or Database)
public class GameServer extends ConnectionServer {
    @Override
    protected GameCommand parseRequest(String input) {
        // Custom binary protocol parsing
    }
    
    @Override
    public GameResponse handleRequest(Request request) {
        // Custom game logic
    }
}

Related Documentation


Next: Explore Core Abstractions to understand the foundational interfaces, or jump to HTTP Server to see a complete protocol implementation.

Clone this wiki locally