Independencia del Dispositivo
Atar la lógica de negocio a dispositivos de E/S específicos es un error costoso — la arquitectura debe ser agnóstica a si los datos vienen de tarjetas, terminales, archivos o APIs.
Por qué importa
En los años 60, los programas se escribían directamente para tarjetas perforadas. Cuando apareció la cinta magnética, el código tenía que reescribirse completamente — no porque cambiara la lógica de negocio, sino porque cambió el dispositivo de entrada. Los sistemas operativos independientes del dispositivo resolvieron esto abstrayendo la E/S en una interfaz uniforme: el mismo programa funcionaba con tarjetas, cintas o discos sin modificación. La misma lección se aplica hoy.
HTTP, stdin, colas de mensajes y cargas de archivos son todos mecanismos de entrega — son el equivalente moderno de las tarjetas perforadas y las cintas. Tu lógica de negocio debería ser tan indiferente a cómo llegan los datos como lo es un SO respecto a qué disco físico está instalado. Cuando la lógica de nómina conoce sys.stdin, es un programa escrito para tarjetas perforadas otra vez.
✗El problema
A payroll function that reads from stdin and writes to a printer device — the business rule is buried inside device-specific code and cannot be reused or tested alone.
Bad
import sys
def run_payroll():
for line in sys.stdin: # tied to stdin
parts = line.strip().split(",")
employee_id = parts[0]
hours, rate = float(parts[1]), float(parts[2])
gross = hours * rate
tax = gross * 0.2
net = gross - tax
with open("/dev/lp0", "w") as printer: # tied to printer device
printer.write(f"Employee {employee_id}: net={net:.2f}\n")
# Switching to REST API input: rewrite run_payroll entirely.
# Switching output from printer to PDF: rewrite run_payroll entirely.
# Testing: must pipe data through stdin and capture printer output.
import { Request, Response } from "express";
export function processPayroll(req: Request, res: Response): void {
const employees = req.body.employees as Array<{ id: string; hours: number; rate: number }>;
const results = employees.map(emp => {
const gross = emp.hours * emp.rate;
const tax = gross * 0.2;
const net = gross - tax;
return `<tr><td>${emp.id}</td><td>${net.toFixed(2)}</td></tr>`;
});
res.send(`<table>${results.join("")}</table>`);
}
// Business rule (tax calculation) is trapped inside an HTTP handler.
// Can't reuse for CLI payroll runs, PDF reports, or batch jobs.
✓La solución
Pure calculation function with no I/O — input source and output destination are wired at the boundary, not inside the business rule.
Good
from dataclasses import dataclass
from decimal import Decimal
@dataclass
class Employee:
id: str
hours: Decimal
rate: Decimal
@dataclass
class PaySlip:
employee_id: str
gross: Decimal
tax: Decimal
net: Decimal
def calculate_payroll(employees: list[Employee]) -> list[PaySlip]:
"""Pure function — no stdin, no files, no HTTP. Device-agnostic."""
slips = []
for emp in employees:
gross = emp.hours * emp.rate
tax = gross * Decimal("0.2")
net = gross - tax
slips.append(PaySlip(emp.id, gross, tax, net))
return slips
# Wired at the boundary — not inside the rule:
# stdin: employees = parse_stdin_csv(sys.stdin)
# REST API: employees = parse_request_body(request.json)
# The same calculate_payroll() call in all cases.
export interface Employee { id: string; hours: number; rate: number; }
export interface PaySlip { employeeId: string; gross: number; tax: number; net: number; }
export function calculatePayroll(employees: Employee[]): PaySlip[] {
return employees.map(emp => {
const gross = emp.hours * emp.rate;
const tax = gross * 0.2;
const net = gross - tax;
return { employeeId: emp.id, gross, tax, net };
});
}
// HTTP boundary wires it for REST:
// app.post("/payroll", (req, res) => {
// const slips = calculatePayroll(req.body.employees);
// res.json(slips);
// });
// CLI boundary wires it for stdin:
// const employees = parseStdinCsv(process.stdin);
// const slips = calculatePayroll(employees);
// printToConsole(slips);
// calculatePayroll is testable with plain arrays — no I/O mocking needed.
💡Conclusión clave
Si tu lógica de negocio sabe qué es HTTP, está acoplada a un mecanismo de entrega que no debería importarle. HTTP, stdin, colas de mensajes y archivos son todos lectores equivalentes de tarjetas perforadas — conéctalos en la frontera y mantén la regla de negocio agnóstica al dispositivo.
🔧 Algunos ejercicios pueden tener errores. Si algo parece incorrecto, usa el botón Feedback (abajo a la derecha) para reportarlo — nos ayuda a corregirlo rápido.
Pista: Si tu lógica de negocio sabe qué es HTTP, está acoplada a un mecanismo de entrega del que no debería saber nada.
✗ Tu versión