- Updated examples in "estructura-web-por-entidades-y-operaciones.md" to reflect correct entity names and directory structures. - Clarified modal usage and component interactions in "impresoras-del-perfil.md". - Adjusted component descriptions in "logotipos-de-companias.md" for consistency. - Revised persistence model details in "normalizacion-de-la-persistencia.md" to remove outdated references and clarify relationships. - Enhanced dialog patterns in "patron-de-dialogos.md" with specific guidelines for field labels and validation. - Specified component reuse in "patron-de-listados-de-entidades.md" to include @repo/rdx-ui components. - Updated implementation status in "personas-y-departamentos.md" to reflect current features and pending tasks. - Clarified project scope and initial setup instructions in "plan-de-desarrollo.md" and "puesta-en-marcha.md". - Improved descriptions of workstation management in "puestos-de-trabajo.md" for better understanding of active/inactive states. - Enhanced resource management details in "recursos-campos-y-plantillas.md" to clarify relationships and pending features. - Revised best practices review in "revision-de-buenas-practicas.md" to reflect current project status and components. - Updated teleworking documentation in "teletrabajo.md" to include references to remote technical assistance. - Clarified user account management in "usuarios-y-acceso.md" to emphasize the importance of account deactivation.
228 lines
12 KiB
Markdown
228 lines
12 KiB
Markdown
# Estructura web por entidades y operaciones
|
|
|
|
Fuente normativa de la organización interna del código web de Metis Desk, referenciada
|
|
desde `AGENTS.md`. Todo módulo nuevo y toda entidad, operación o componente que se añada
|
|
a un módulo existente debe seguir esta estructura. Los módulos `core`, `identity`, `assets`,
|
|
`resources` y `companies` proporcionan ejemplos de su aplicación.
|
|
|
|
## Regla de organización
|
|
|
|
El orden obligatorio es módulo → entidad o área funcional → operación → responsabilidad:
|
|
|
|
```text
|
|
modules/<module>/src/web/<entity>/<operation>/<responsibility>/
|
|
```
|
|
|
|
Por ejemplo, `web/people/create/ui/`, no `web/create/people/` ni un directorio global
|
|
`web/components/` para componentes de entidades distintas. Utilizar nombres en inglés y
|
|
kebab-case. Las áreas sin una entidad de negocio propia, como `dashboard` o `workspace`,
|
|
siguen el mismo orden con operaciones descriptivas como `view`.
|
|
|
|
Esta regla organiza el código del navegador, no prescribe nuevas entidades DDD ni cambia
|
|
la estructura de `api` y `common`, la composición del servidor o los contratos HTTP.
|
|
No obliga a crear carpetas para todas las operaciones posibles ni todas las responsabilidades:
|
|
cada directorio debe contener una responsabilidad real de la implementación actual.
|
|
|
|
## Ubicación de las responsabilidades
|
|
|
|
Ejemplo real de personas en `modules/companies/src/web/` (ficheros seleccionados):
|
|
|
|
```text
|
|
people/
|
|
detail/
|
|
ui/
|
|
person-details.tsx
|
|
current-workstations.tsx
|
|
index.ts
|
|
index.ts
|
|
list/
|
|
ui/
|
|
people-section.tsx
|
|
index.ts
|
|
index.ts
|
|
shared/
|
|
api/
|
|
save-person.api.ts
|
|
index.ts
|
|
adapters/
|
|
build-person-request.ts
|
|
index.ts
|
|
controllers/
|
|
use-person-form-controller.ts
|
|
index.ts
|
|
schemas/
|
|
person-form.schema.ts
|
|
index.ts
|
|
types/
|
|
person-form-props.ts
|
|
index.ts
|
|
ui/
|
|
person-form.tsx
|
|
person-editor-dialog.tsx
|
|
person-photo-field.tsx
|
|
index.ts
|
|
index.ts
|
|
index.ts
|
|
```
|
|
|
|
El árbol muestra las ubicaciones, no implica exportar todos los ficheros. Los índices declaran
|
|
únicamente los símbolos que sus consumidores necesitan. Las operaciones sin una responsabilidad
|
|
propia no reciben directorios vacíos: el editor común de alta y edición permanece
|
|
en `people/shared/ui/`.
|
|
|
|
- `ui`: componentes y formularios. Un componente principal por fichero, sin nuevas
|
|
subdivisiones de JSX trivial.
|
|
- `api`: funciones de acceso HTTP y claves de consultas de su ámbito, sin dependencias de UI.
|
|
- `queries`: claves y configuración de TanStack Query cuando ya tengan una responsabilidad
|
|
separada, como `health/shared/queries/core.query-keys.ts`; no duplicarlas también en `api`.
|
|
- `controllers`: coordinación React de una operación cuando necesite una responsabilidad
|
|
propia, como `catalog/shared/controllers/use-catalog-save.ts`.
|
|
- `hooks`: comportamiento React reutilizable o una responsabilidad React identificable,
|
|
como `work-environments/detail/hooks/use-environment-queries.ts`.
|
|
- `types` y `helpers`: contratos locales y cálculos, próximos a sus consumidores.
|
|
- `schemas`, `adapters`, `entities` o `utils`: solo cuando existan validaciones de formulario,
|
|
adaptaciones, modelos de UI o utilidades concretas que requieran esos ficheros. No duplicar
|
|
DTOs ni schemas HTTP de `common`, trasladar entidades de dominio a `web` o crear cajones
|
|
de sastre genéricos.
|
|
- `<entity>/shared`: código utilizado por varias operaciones de la misma entidad y pruebas
|
|
que cubren conjuntamente esas operaciones.
|
|
- `web/shared`: recursos de varias entidades del módulo, incluidas traducciones y componentes
|
|
comunes. No sustituye a `common`, que sigue conteniendo contratos y schemas compartidos con
|
|
el servidor.
|
|
|
|
Crear únicamente los subdirectorios que la operación necesite. Una pantalla sencilla no
|
|
requiere un controller, hook o adapter artificial. Los elementos privados inseparables del
|
|
componente pueden permanecer junto a él; separar por responsabilidad, no por tamaño ni
|
|
para completar el árbol de un ejemplo.
|
|
|
|
## Entidades y ámbitos compartidos
|
|
|
|
Los listados, detalles, altas específicas e historiales se sitúan en `list`, `detail`, `create`
|
|
y `history`. Las operaciones particulares conservan nombres explícitos, como `change-logo`,
|
|
`change`, `start`, `update` o `end`.
|
|
|
|
Áreas antes agrupadas por una categoría amplia quedan identificadas por sus entidades:
|
|
`mailboxes`, `mailbox-uses` y `mail-configurations`; `work-profiles` y `work-environments`;
|
|
`processes` y `process-templates`.
|
|
Los componentes de dashboard y workspace siguen el mismo criterio funcional.
|
|
|
|
Un nuevo módulo conserva los niveles de entidad y operación aunque inicialmente contenga
|
|
una sola entidad. Para una entidad nueva, comprobar antes si pertenece a un módulo existente:
|
|
la estructura web no es un motivo para crear un paquete independiente. Para una operación
|
|
nueva, utilizar un nombre que describa su intención y no duplicar operaciones que ya cubran
|
|
el mismo comportamiento.
|
|
|
|
Ubicar código privado de una operación dentro de ella. Promoverlo a `<entity>/shared` cuando
|
|
lo consuman varias operaciones de esa entidad, o a `web/shared` cuando lo consuman varias
|
|
entidades del módulo. No usar `shared` como ubicación por defecto ni anticipar reutilización.
|
|
Las traducciones siguen [su estándar](traducciones.md); los componentes visuales genéricos
|
|
compartidos siguen [componentes de interfaz](componentes-de-interfaz.md).
|
|
|
|
Los formularios y diálogos que sirven tanto para crear como para editar se mantienen en el
|
|
`shared/ui` de su entidad. No se duplican ni se introducen wrappers de alta y edición sin
|
|
comportamiento propio. El diálogo de inicio de un proceso pertenece a `processes/start`, aunque
|
|
se abra desde otra pantalla; el consumidor no determina la identidad del componente.
|
|
|
|
Situar el acceso HTTP en el ámbito más específico que corresponda a sus consumidores:
|
|
`<entity>/<operation>/api`, `<entity>/shared/api` o `web/shared/api`. Los clientes existentes
|
|
que abarcan varias entidades se separan por sus operaciones reales. Por ejemplo,
|
|
`users/create/api/create-user.api.ts`, `people/shared/api/save-person.api.ts` y
|
|
`process-templates/list/api/list-process-templates.api.ts`. El transporte, errores y claves
|
|
compartidos no se duplican en cada operación. Las composiciones públicas existentes de
|
|
identidad e inventario conservan sus consumidores externos, pero los controllers del propio
|
|
módulo utilizan las funciones de su operación. No dividir clientes pequeños y cohesivos
|
|
ni alterar firmas, queries o contratos únicamente para reproducir el árbol de ejemplo.
|
|
|
|
## Separación lógica de la operación
|
|
|
|
El ejemplo de `customer-invoices` orienta la separación de responsabilidades, no introduce
|
|
sus dependencias, servicios ni arquitectura de persistencia en Metis Desk:
|
|
|
|
- `ui`: JSX, traducciones de presentación, accesibilidad y adaptación de eventos del DOM.
|
|
Los modales y formularios significativos conservan su componente independiente.
|
|
- `controllers`: hooks `use-…-controller` para coordinación de formularios, queries,
|
|
mutaciones, invalidación, altas relacionadas y callbacks. Los detalles de ciclo de vida
|
|
utilizados para evitar callbacks después del desmontaje quedan privados del controller.
|
|
- `adapters`: transformación de valores de formulario a entradas de la operación cuando
|
|
exista una conversión real, como vacíos a `null` o líneas a instrucciones. No envolver
|
|
objetos idénticos ni duplicar la validación de los schemas.
|
|
- `helpers`: selección, filtrado y cálculos sin acceso a React, transporte o persistencia.
|
|
Mantener nombres que identifiquen su propósito y su entidad.
|
|
- `schemas` y `types`: validación exclusiva del formulario y contratos locales inferidos
|
|
de su schema. Los contratos HTTP siguen perteneciendo a `common`.
|
|
- `api` y `queries`: transporte validado y claves de caché separadas de la presentación.
|
|
|
|
Un controller no es una capa nueva de negocio ni un repositorio: debe conservar la operación
|
|
existente, no ocultar efectos tras nombres genéricos. No necesita `useMemo` o `useCallback`
|
|
por defecto. Formularios pequeños y consultas simples pueden permanecer en su componente
|
|
cuando no mezclen responsabilidades significativas; un hook existente que ya encapsule
|
|
la consulta no requiere un segundo controller que solo lo envuelva.
|
|
|
|
Los controllers, adapters y helpers no importan implementaciones de UI para acceder a
|
|
cálculos o constantes. Utilizar imports directos entre APIs cuando pasar por un índice
|
|
agregado reintroduzca la composición pública en su propio transporte. Los índices exponen
|
|
solo contratos de su responsabilidad utilizados por otras unidades, no reexports internos
|
|
de compatibilidad desde el antiguo componente.
|
|
|
|
## Índices y dependencias
|
|
|
|
Cada nivel debe tener un `index.ts` solo cuando contenga símbolos exportables para otras
|
|
unidades con consumidores reales. El índice enumera ese contrato, no todos los exports
|
|
de los ficheros internos. No se crean índices vacíos, exportaciones `*`, exportaciones de tests ni
|
|
índices para detalles privados utilizados exclusivamente dentro de su propio directorio.
|
|
Los tipos se reexportan mediante `export type`.
|
|
|
|
Los consumidores utilizan el índice de la unidad correspondiente cuando ello no añade ciclos.
|
|
Los imports entre ficheros de la misma unidad permanecen directos. También se conservan
|
|
imports directos específicos cuando un índice agregado añadiría un ciclo de dependencias,
|
|
por ejemplo en flujos de altas anidadas. No se amplía la API pública para resolver esas
|
|
dependencias internas.
|
|
|
|
`web/index.ts` compone los índices internos y define los símbolos públicos de
|
|
`@metis/<module>/client` cuando haya consumidores externos. Las aplicaciones y otros módulos
|
|
utilizan ese contrato, no caminos internos del módulo. Al ampliar un módulo existente,
|
|
mantener sus contratos públicos salvo un cambio deliberado con revisión de consumidores.
|
|
No añadir reexports de compatibilidad para caminos internos antiguos cuando todos sus
|
|
consumidores puedan actualizarse de forma segura.
|
|
|
|
## Pruebas y mantenimiento
|
|
|
|
Las pruebas existentes acompañan al ámbito que comprueban. Los scripts de cada paquete
|
|
referencian sus nuevas ubicaciones; los imports, traducciones y enlaces de documentación
|
|
se actualizan junto con los movimientos.
|
|
|
|
Las pruebas nuevas específicas de una operación viven junto a la responsabilidad que
|
|
comprueban. Las que cubren varias operaciones se sitúan en el `shared` correspondiente.
|
|
No exportar tests desde los índices. Registrar los nuevos ficheros en el script del paquete
|
|
cuando utilice una enumeración explícita.
|
|
|
|
## Lista de comprobación para nuevos desarrollos
|
|
|
|
1. Consultar esta guía, las capas aplicables y las implementaciones equivalentes existentes.
|
|
2. Identificar módulo, entidad o área funcional y operación antes de crear ficheros.
|
|
3. Crear solo responsabilidades necesarias y elegir el ámbito compartido por sus consumidores.
|
|
4. Reutilizar formularios de alta/edición y primitivas visuales; separar modales significativos
|
|
en su propio `.tsx` sin trasladar todo su contenido al listado.
|
|
5. Definir índices explícitos únicamente donde haya contratos exportables a otras unidades;
|
|
comprobar imports de tipos, consumidores y ausencia de nuevos ciclos.
|
|
6. Mantener DTOs y schemas compartidos en `common`, traducciones es/en y acceso HTTP fuera
|
|
de la presentación; no introducir capas o dependencias para completar una estructura.
|
|
7. Añadir pruebas observables, actualizar scripts y enlaces, y revisar el diff completo.
|
|
8. Ejecutar comprobaciones dirigidas al paquete o las cinco validaciones globales cuando
|
|
el cambio sea transversal, según [pruebas](arquitectura/pruebas.md).
|
|
|
|
## Alcance de la migración realizada
|
|
|
|
La reorganización inicial trasladó ficheros y ajustó sus imports, sin cambiar comportamiento
|
|
ni contratos. No extrajo lógica nueva a controllers, hooks, schemas o adapters; esa auditoría
|
|
se aplazó por decisión del usuario. La auditoría posterior separó la lógica de formularios,
|
|
listados y coordinación en los cinco módulos, excluyendo `apps/web/src` y vendor. Su alcance,
|
|
criterios y comprobaciones se registran en [la auditoría del cliente](auditoria-de-separacion-logica-del-cliente.md).
|
|
La limitación de la migración inicial no exime al código nuevo de las reglas de separación
|
|
de responsabilidades de `AGENTS.md`.
|
|
|
|
Para futuros cambios, consultar también [componentes de interfaz](componentes-de-interfaz.md),
|
|
[inventario de modales](inventario-de-modales.md), [patrón de listados](patron-de-listados-de-entidades.md)
|
|
y [pruebas](arquitectura/pruebas.md). No trasladar una responsabilidad compartida a una
|
|
operación concreta ni extraer lógica nueva únicamente para reproducir un árbol de directorios.
|