Arquitectura del proyecto: IPS Java/Swing

Versión: 1.1 | Fecha: 22 de septiembre de 2025 | Autor: Marcos Losada García

Cambios: Añadido Apéndice A con un caso de uso práctico.

1. Introducción

1.1. Propósito

Este documento describe la arquitectura de software de la aplicación. Su objetivo es proporcionar una visión general de la estructura del sistema, los principios de diseño aplicados y los patrones arquitectónicos utilizados. Servirá como guía para el desarrollo, mantenimiento y evolución de la aplicación.

1.2. Alcance

La arquitectura descrita aplica a una aplicación de escritorio monolítica desarrollada en Java con la librería gráfica Swing. El diseño se centra en la escalabilidad, mantenibilidad y un alto grado de desacoplamiento entre sus componentes.

2. Visión Arquitectónica General

La aplicación se basa en una arquitectura Monolítica Modular, organizada mediante el principio Package-by-Layered-Feature.

Este enfoque combina la simplicidad de despliegue de un monolito con la escalabilidad y mantenibilidad de un diseño modular.

3. Estructura de Paquetes

La estructura de paquetes es el reflejo directo de la arquitectura.

com.losadagm.demo
├── config/                 // Configuración global (ej. logging, propiedades).
├── shared/                 // Clases de utilidad compartidas por toda la aplicación.
│   └── db/                 // Infraestructura de base de datos (DatabaseManager).
├── main/                   // Punto de entrada de la aplicación.
│   └── AppMain.java
└── user/                   // << MÓDULO DE FUNCIONALIDAD "USER" >>
    ├── mvc/                // Capa de Presentación (Patrón MVC + Observer).
    │   ├── UserModel.java
    │   ├── UserView.java
    │   └── UserController.java
    ├── UserFacade.java     // Fachada pública del módulo.
    └── internal/           // Implementación interna, no visible para otros módulos.
        ├── domain/         // Entidades de dominio (POJOs).
        │   └── User.java
        ├── service/        // Lógica de negocio (Patrón Service Layer).
        │   └── UserServiceImpl.java
        └── data/           // Acceso a datos (Patrón DAO).
            ├── UserDao.java
            └── UserDaoDbImpl.java

4. Descripción de las Capas y Patrones

4.1. Capa de Presentación (Presentation Layer)

Responsabilidad: Mostrar la interfaz de usuario (UI) y capturar las interacciones del usuario.

Patrones Clave: MVC (Model-View-Controller) y Observer.

4.2. Capa de Servicio (Service Layer)

Responsabilidad: Contener la lógica de negocio central de la aplicación.

Patrones Clave: Fachada (Facade) y Service Layer.

4.3. Capa de Datos (Data Layer)

Responsabilidad: Abstraer el acceso a la fuente de datos (JDBC).

Patrones Clave: DAO (Data Access Object).

5. Flujo de Datos y Control

Un flujo de interacción típico (ej. refrescar una lista de usuarios) sigue estos pasos:

  1. View: El usuario hace clic en el botón "Refrescar". La UserView notifica al UserController.
  2. Controller: El UserController llama al método getAllUsers() de la UserFacade.
  3. Facade: La UserFacade delega la llamada a su UserService interno.
  4. Service: El UserServiceImpl llama al método findAll() de su UserDao.
  5. DAO: El UserDaoDbImpl ejecuta la consulta SQL a través del DatabaseManager.
  6. Retorno: Los datos viajan de vuelta por las mismas capas, el UserController actualiza el UserModel.
  7. Observer: El UserModel notifica a su Observer (la UserView) que su estado ha cambiado.
  8. View: La UserView se actualiza, redibujando la tabla con los nuevos datos.

6. Principios de Diseño Clave

Apéndice A: Aplicación Práctica de la Arquitectura - Caso de Uso

A.1. Escenario del Caso de Uso

Se considera un escenario donde la aplicación tiene una interfaz de usuario (UI) simple, consistente en un único panel de control (Dashboard), pero maneja una lógica de negocio compleja que involucra múltiples entidades de dominio (User, Product, Order).

Requisito del caso de uso: El usuario hace clic en un botón "Crear Nuevo Pedido" en el Dashboard. Esta acción requiere obtener información del usuario, verificar el stock del producto y finalmente, crear un nuevo pedido en la base de datos.

A.2. Estructura de Paquetes en este Escenario

En este caso, la arquitectura se manifiesta de la siguiente manera: solo la funcionalidad del Dashboard tiene una capa de presentación mvc. Las demás funcionalidades (user, product, order) exponen su lógica a través de fachadas, pero no tienen vistas propias.

com.losadagm.demo
│
├── dashboard/                 // Módulo con UI.
│   ├── mvc/
│   │   ├── DashboardModel.java
│   │   ├── DashboardView.java
│   │   └── DashboardController.java
│   └── DashboardFacade.java
│
├── user/                      // Módulo "sin cabeza" (headless).
│   ├── UserFacade.java
│   └── internal/
│
├── product/                   // Módulo "sin cabeza".
│   ├── ProductFacade.java
│   └── internal/
│
└── order/                     // Módulo "sin cabeza".
    ├── OrderFacade.java
    └── internal/

A.3. Flujo de Control Detallado

El flujo para "Crear Nuevo Pedido" demuestra cómo las distintas funcionalidades colaboran sin acoplarse directamente, orquestadas por el DashboardController y las fachadas.

Evento de UI (DashboardView):

El usuario introduce los datos del pedido (ID de usuario, SKU del producto, cantidad) y hace clic en "Crear Pedido". La DashboardView captura el evento y llama a su controlador: controller.handleCreateOrderRequest(userId, productSku, quantity);.

Orquestación en el Controlador (DashboardController):

El DashboardController recibe la petición. Su rol no es saber cómo se crea un pedido, sino delegar la tarea a los expertos. Invoca a la fachada de pedidos: orderFacade.createNewOrder(userId, productSku, quantity);.

Lógica de Negocio en la Fachada (OrderFacade):

OrderFacade.createNewOrder() es el método que orquesta la operación completa. Para ello, colabora con otras fachadas, pero nunca con sus detalles internos.

Actualización de la UI:

La OrderFacade devuelve un resultado. El DashboardController lo recibe y le pide a la DashboardFacade que refresque los datos del panel. Esta fachada obtiene los datos actualizados, los pone en el DashboardModel, y el modelo notifica a la DashboardView (su Observer) para que se redibuje y muestre el estado más reciente.

A.4. Conclusiones del Caso de Uso

Este ejemplo demuestra los beneficios clave de la arquitectura: