Skip to main content

System Overview

The Conversor de Unidades Remoto implements a three-tier client-server architecture using ZeroC Ice middleware for remote procedure calls (RPC). This design separates concerns and enables distributed computing across network boundaries.

Frontend Layer

Vanilla JavaScript single-page application with real-time conversion and theming support

Flask Server

Python web server that serves static files and proxies API requests to the ICE backend

ICE Server

ZeroC Ice RPC server handling business logic for unit conversions

Architecture Diagram

Component Interaction

1

User Input

User enters a value and selects conversion units in the browser interface. JavaScript captures the input through event listeners.
2

HTTP Request

Frontend sends a POST request to Flask server at /api/convert with JSON payload:
{
  "categoria": "temperatura",
  "valor": 100,
  "desde": "celsius",
  "hasta": "fahrenheit"
}
3

ICE Proxy Call

Flask server uses ICE proxy to invoke remote method on ICE server:
resultado = self.proxy.convertirTemperatura(valor, desde, hasta)
4

Business Logic Execution

ICE server executes conversion algorithm and returns result as a double-precision float.
5

Response Chain

Result flows back through Flask to frontend as JSON:
{"resultado": 212.0}
6

UI Update

JavaScript updates the display and adds the conversion to history with smooth animations.

Technology Stack

Backend Technologies

ComponentTechnologyPurpose
RPC MiddlewareZeroC Ice 3.7+Remote procedure calls, marshalling, network transport
Web FrameworkFlask 2.0+HTTP server, static file serving, REST API
Interface DefinitionSlice LanguageLanguage-agnostic RPC contract
Code Generationslice2pyPython stubs/skeletons from Slice

Frontend Technologies

ComponentTechnologyPurpose
UI FrameworkVanilla JavaScriptDOM manipulation, event handling
HTTP ClientFetch APIAsynchronous API requests
StylingCSS3 Custom PropertiesTheming, responsive design
IconsBootstrap IconsVisual elements

Design Patterns

The application demonstrates several key architectural patterns:
  • Proxy Pattern: Flask server acts as a proxy between HTTP clients and ICE server
  • Servant Pattern: ICE object implementation (servant) handles remote invocations
  • Strategy Pattern: Base unit conversion strategy (normalize to base unit, then convert)
  • Repository Pattern: Separation of business logic (ICE) from presentation (Flask/JS)

Network Communication

Port Configuration

ICE Server

Port: 10000
Protocol: TCP (ZeroC Ice)
Binding: localhost (default)
Endpoint: default -p 10000

Flask Server

Port: 5000 (configurable via PORT env var)
Protocol: HTTP
Binding: localhost (configurable via HOST env var)
Routes: /, /api/convert, /api/status

Connection String

The Flask server connects to ICE using a stringified proxy reference:
# From web_server.py:37-38
base = self.communicator.stringToProxy(
    "ConversorUnidades:default -p 10000"
)
This string encodes:
  • Object Identity: ConversorUnidades - the name of the remote object
  • Endpoint: default -p 10000 - TCP on port 10000

Error Handling Strategy

Slice-defined exceptions for domain errors:
exception UnidadInvalidaException {
    string mensaje;
};
Thrown when invalid units are provided.

Data Flow Example

Here’s a complete trace of converting 100°C to Fahrenheit:
1

Frontend Capture

// app.js:57-84
const response = await fetch('/api/convert', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        categoria: 'temperatura',
        valor: 100,
        desde: 'celsius',
        hasta: 'fahrenheit'
    })
});
2

Flask Routing

# web_server.py:99-119
@app.route('/api/convert', methods=['POST'])
def api_convert():
    data = request.get_json()
    categoria = data.get('categoria', '').lower()  # 'temperatura'
    valor = float(data.get('valor', 0))            # 100.0
    desde = data.get('desde', '').lower()          # 'celsius'
    hasta = data.get('hasta', '').lower()          # 'fahrenheit'
    
    if categoria == 'temperatura':
        resultado = cliente.convert_temperatura(valor, desde, hasta)
3

ICE Invocation

# web_server.py:59-60
def convert_temperatura(self, valor, desde, hasta):
    return self.proxy.convertirTemperatura(valor, desde, hasta)
4

Servant Execution

# server.py:27-45
def convertirTemperatura(self, valor, desde, hasta, current=None):
    # valor=100, desde='celsius', hasta='fahrenheit'
    self._validar(desde, hasta, {"celsius", "fahrenheit", "kelvin"}, "temperatura")
    
    # Convert to Celsius (already in Celsius)
    en_celsius = valor  # 100.0
    
    # Convert to Fahrenheit
    if hasta == "fahrenheit":
        return en_celsius * 9 / 5 + 32  # Returns: 212.0
5

Response Packaging

# web_server.py:129
return jsonify({'resultado': resultado})  # {'resultado': 212.0}
6

UI Rendering

// app.js:128-135
const res = await convert(currentCat, valor, desde, hasta);
const formatted = parseFloat(res.toFixed(6)).toString();

resEl.textContent = formatted;  // "212"
unitEl.textContent = labels[currentCat][hasta];  // "°F"

Deployment Considerations

Both the ICE server and Flask server must be running for the application to function:
  1. Start ICE server first: python3 backend/server.py
  2. Then start Flask: python3 backend/web_server.py
The Flask server will attempt to connect to ICE on startup but will continue running even if the connection fails.

Environment Variables

Flask server configuration (from web_server.py:184-186):
host = os.getenv('HOST', 'localhost')
port = int(os.getenv('PORT', '5000'))
debug = os.getenv('DEBUG', 'true').lower() == 'true'

Security Considerations

Current Implementation: Both servers bind to localhost by default, restricting access to local machine.Production Recommendations:
  • Add authentication/authorization to Flask API endpoints
  • Use ICE SSL endpoints for encrypted communication
  • Implement rate limiting on conversion endpoints
  • Validate and sanitize all user inputs
  • Consider using environment-specific Ice configurations

Performance Characteristics

  • Latency: Sub-millisecond for local ICE calls
  • Throughput: Limited by Flask’s development server (use Gunicorn/uWSGI for production)
  • Concurrency: ICE server handles concurrent requests via thread pool
  • Caching: No caching implemented (stateless conversions)

Next Steps

ICE Middleware

Deep dive into ZeroC Ice implementation

Flask Server

Explore REST API and proxy implementation

Frontend

Learn about the JavaScript architecture

Getting Started

Set up the application locally

Build docs developers (and LLMs) love