🗂️
Estructura del Workspace
agrisense-workspace/
agrisense-workspace/
apps/ # Aplicaciones deployables
│ portal-cooperativas/ # Next.js 14 — portal B2B
│ │ project.json # tags: type:app, scope:web
│ │ src/
│ └admin-panel/ # React SPA — panel interno
│ project.json # tags: type:app, scope:admin
│ src/
libs/ # Librerias compartidas
│ shared/ # scope:shared — disponible a todos
│ │ ui-components/ # type:ui — Design System AgriSense
│ │ │ src/lib/Button.tsx
│ │ │ src/lib/Chart.tsx
│ │ │ src/lib/SensorCard.tsx
│ │ util-formatting/ # type:util — fechas, unidades, i18n
│ │ util-validators/ # type:util — validators puros
│ │ data-access-auth/ # type:data-access — tokens, session
│ web/ # scope:web — solo portal cooperativas
│ │ feature-dashboard/ # type:feature — dashboard sensores
│ │ feature-alertas/ # type:feature — alertas y notifs
│ │ feature-informes/ # type:feature — export PDF/Excel
│ │ data-access-sensors/ # type:data-access — API sensores IoT
│ └admin/ # scope:admin — solo panel interno
│ feature-usuarios/ # type:feature — CRUD cooperativas
│ feature-facturacion/ # type:feature — billing y planes
tools/ # Generators y executors custom
│ generators/
│ feature-lib/ # nx g agrisense:feature-lib
nx.json # Configuracion global + NxCloud
tsconfig.base.json # Path aliases compartidos
.eslintrc.json # Module boundary rules
.github/workflows/ci.yml # CI con affected commands
📦
Tipos de Libreria — AgriSense
feature
Componentes inteligentes y logica de negocio especifica
feature-dashboard
ui
Componentes presentacionales puros sin estado global
ui-components
data-access
Llamadas API, gestion de estado y caches de datos
data-access-sensors
util
Funciones puras, helpers, formatters sin dependencias UI
util-formatting
shell
Bootstrapping, routing raiz y providers globales de la app
shell-portal
⚙️
Configuracion nx.json con NxCloud
📄
nx.json
JSON
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"npmScope": "agrisense",
// Solo testear/construir lo que cambia respecto a main
"affected": { "defaultBase": "main" },
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud", // Cache remoto compartido entre devs
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"],
"accessToken": "${NX_CLOUD_ACCESS_TOKEN}",
"parallel": 3
}
}
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"], // Construye deps primero
"inputs": ["production", "^production"],
"cache": true
},
"test": {
"inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
"cache": true
},
"lint": {
"inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
"cache": true
}
},
"namedInputs": {
"production": [
"default",
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
"!{projectRoot}/tsconfig.spec.json"
],
"sharedGlobals": [
"{workspaceRoot}/tsconfig.base.json"
]
}
}
🛡️
.eslintrc.json — Module Boundary Rules
JSON
{
"plugins": ["@nx"],
"overrides": [{
"rules": {
"@nx/enforce-module-boundaries": ["error", {
"depConstraints": [
// Apps pueden importar cualquier tipo de lib
{ "sourceTag": "type:app",
"onlyDependOnLibsWithTags": ["type:feature","type:ui","type:data-access","type:util"] },
// Feature solo puede usar ui, data-access y util
{ "sourceTag": "type:feature",
"onlyDependOnLibsWithTags": ["type:ui","type:data-access","type:util"] },
// UI solo puede importar util (sin logica de negocio)
{ "sourceTag": "type:ui",
"onlyDependOnLibsWithTags": ["type:ui", "type:util"] },
// Scope: web no puede importar libs de admin (y viceversa)
{ "sourceTag": "scope:web",
"onlyDependOnLibsWithTags": ["scope:web", "scope:shared"] },
{ "sourceTag": "scope:admin",
"onlyDependOnLibsWithTags": ["scope:admin", "scope:shared"] }
]
}]
}
}]
}
🔒
Matriz de Limites de Modulo
| Tipo fuente | Puede importar | Ejemplo en AgriSense |
|---|---|---|
| type:app |
type:feature
type:ui
type:data-access
type:util
|
portal-cooperativas importa feature-dashboard |
| type:feature |
type:ui
type:data-access
type:util
|
feature-alertas usa ui-components + data-access-sensors |
| type:ui |
type:ui
type:util
|
SensorCard solo usa util-formatting para labels |
| type:data-access |
type:data-access
type:util
|
data-access-sensors usa util-validators para schemas |
| type:util |
type:util
|
util-formatting no importa nada externo |
| scope:web |
scope:web
scope:shared
|
portal-cooperativas no puede tocar libs de admin |
🚀
CI Pipeline con Affected Commands
Step 1
Checkout
~15s
→
Step 2
npm ci
~45s
→
Step 3
affected lint
~30s
→
Step 4
affected test
~60s
→
Step 5
affected build
~90s
❌ Antes — Build Total
🔨Build portal-cooperativas completo
🔨Build admin-panel completo
🧪Tests de las 11 libs (todas)
🔍Lint de todo el workspace
⚠️Sin cache — siempre reconstruye
25 min
por cada PR
✅ Ahora — Affected + Cache
⚡Solo proyectos afectados por el diff
💚Cache hit para libs no modificadas
⚡Paralelo x3 (lint, test, build)
☁️NxCloud comparte cache entre devs
🔄SHA derivation automatica (nrwl/nx-set-shas)
~4 min
promedio por PR tipico
⚡
.github/workflows/ci.yml
YAML
name: CI AgriSense
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Necesario para calcular affected
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Derive SHAs for affected
uses: nrwl/nx-set-shas@v4 # Calcula base SHA automaticamente
- name: Lint affected
run: npx nx affected -t lint --parallel=3
- name: Test affected
run: npx nx affected -t test --parallel=3 --configuration=ci
- name: Build affected
run: npx nx affected -t build --parallel=3
🔮
Custom Generator — Feature Lib AgriSense
1
Invocar el generator
nx g agrisense:feature-lib --name=exportar-csv --scope=web
2
Crea la libreria con tags correctos
Genera libs/web/feature-exportar-csv/ con tags: type:feature, scope:web automaticamente. Sin riesgo de olvidar los tags.
3
Estructura estandar garantizada
Incluye src/index.ts, jest.config.ts, tsconfig.json y project.json preconfigurados. Listo para desarrollar.
4
Path alias registrado en tsconfig.base.json
Anade "@agrisense/feature-exportar-csv": ["libs/web/feature-exportar-csv/src/index.ts"] automaticamente.
🔧
tools/generators/feature-lib/index.ts
TypeScript
import { Tree, formatFiles, generateFiles, names, joinPathFragments } from "@nx/devkit";
import { libraryGenerator } from "@nx/react";
interface FeatureLibSchema {
name: string;
scope: "web" | "admin" | "shared";
}
export default async function featureLibGenerator(
tree: Tree,
options: FeatureLibSchema
) {
const { name, scope } = options;
await libraryGenerator(tree, {
name: `feature-${name}`,
directory: `libs/${scope}/feature-${name}`,
// Tags: garantizan que module boundaries funcionen
tags: `type:feature,scope:${scope}`,
style: "css",
unitTestRunner: "jest",
linter: "eslint",
skipFormat: true
});
await formatFiles(tree);
return () => console.log(`✅ Lib feature-${name} creada en scope:${scope}`);
}
⚡
Comandos Esenciales del Dia a Dia
Comando
Descripcion
Uso
nx affected -t test --base=main
Ejecuta tests solo en proyectos afectados por cambios vs main
CI
nx graph
Visualiza el grafo de dependencias interactivo del workspace
DEV
nx g agrisense:feature-lib --name=X --scope=web
Genera nueva feature lib con tags y estructura estandar
DEV
nx build portal-cooperativas --configuration=production
Build de produccion del portal (usa cache si no hay cambios)
CACHE
nx reset
Limpia cache local cuando hay problemas de estado inconsistente
CACHE
nx migrate latest && nx migrate --run-migrations
Actualiza Nx y todos los plugins a la ultima version estable
MAINT
📊
ROI Estimado para AgriSense
-84%
Tiempo CI por PR
De 25 min a ~4 min
con affected + NxCloud cache
con affected + NxCloud cache
3.360h
Horas dev ahorradas/ano
10 devs × 8 PRs/sem
× 21 min ahorrados × 52 sem
× 21 min ahorrados × 52 sem
~42k€
Valor monetario/ano
3.360h × 12,5 €/h (coste medio)
sin contar frustración dev
sin contar frustración dev
Plan de migracion — 4 semanas
SEMANA 1
Init workspace Nx. Mover apps existentes a apps/. Configurar nx.json base.
SEMANA 2
Extraer ui-components y utils compartidos. Definir tags y module boundaries.
SEMANA 3
Crear feature libs por dominio. Activar NxCloud. Primer CI con affected.
SEMANA 4
Custom generators. Documentar limites. Training equipo. Go-live completo.